react-native-nami-sdk 3.3.2-3 → 3.3.2-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.
Files changed (35) hide show
  1. package/android/build.gradle +2 -2
  2. package/android/src/main/AndroidManifest.xml +14 -1
  3. package/android/src/main/java/com/namiml/reactnative/NamiBridgePackage.java +15 -0
  4. package/android/src/main/java/com/namiml/reactnative/NamiFlowManagerBridge.kt +22 -17
  5. package/android/src/main/java/com/namiml/reactnative/NamiOverlayControlBridge.kt +171 -0
  6. package/android/src/main/java/com/namiml/reactnative/ReactOverlayActivity.kt +29 -0
  7. package/android/src/main/res/values/styles.xml +10 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/specs/NativeNamiOverlayControl.d.ts +9 -0
  10. package/dist/src/NamiOverlayControl.d.ts +9 -0
  11. package/dist/src/NamiOverlayHost.d.ts +1 -0
  12. package/dist/src/overlay.d.ts +4 -0
  13. package/dist/src/registerOverlay.d.ts +1 -0
  14. package/dist/src/version.d.ts +1 -1
  15. package/index.ts +1 -0
  16. package/ios/Nami.swift +3 -3
  17. package/ios/NamiCampaignManagerBridge.m +1 -1
  18. package/ios/NamiCampaignManagerBridge.swift +19 -10
  19. package/ios/NamiCustomerManager.m +1 -1
  20. package/ios/NamiCustomerManager.swift +19 -10
  21. package/ios/NamiEntitlementManagerBridge.m +1 -1
  22. package/ios/NamiEntitlementManagerBridge.swift +17 -11
  23. package/ios/NamiFlowManagerBridge.m +1 -1
  24. package/ios/NamiFlowManagerBridge.swift +20 -21
  25. package/ios/NamiOverlayControlBridge.m +17 -0
  26. package/ios/NamiOverlayControlBridge.swift +136 -0
  27. package/ios/NamiPaywallManagerBridge.m +1 -1
  28. package/ios/NamiPaywallManagerBridge.swift +22 -19
  29. package/ios/NamiPurchaseManagerBridge.m +1 -1
  30. package/ios/NamiPurchaseManagerBridge.swift +20 -7
  31. package/package.json +8 -1
  32. package/react-native-nami-sdk.podspec +1 -1
  33. package/specs/NativeNamiOverlayControl.ts +9 -0
  34. package/src/NamiOverlayControl.tsx +48 -0
  35. package/src/version.ts +1 -1
@@ -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.2.2"
89
- amazonImplementation "com.namiml:sdk-amazon:3.3.2.2"
88
+ playImplementation "com.namiml:sdk-android:3.3.2.3"
89
+ amazonImplementation "com.namiml:sdk-amazon:3.3.2.3"
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"
@@ -1,3 +1,16 @@
1
- <manifest package="com.namiml.reactnative">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.namiml.reactnative">
2
3
 
4
+
5
+ <application>
6
+ <activity
7
+ android:name=".ReactOverlayActivity"
8
+ android:theme="@style/Theme.TransparentReact"
9
+ android:exported="false"
10
+ android:launchMode="singleTop"
11
+ android:taskAffinity=""
12
+ android:noHistory="true"
13
+ android:excludeFromRecents="true"
14
+ android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" />
15
+ </application>
3
16
  </manifest>
@@ -29,6 +29,8 @@ public class NamiBridgePackage extends TurboReactPackage {
29
29
  case NamiPaywallManagerBridgeModule.NAME -> new NamiPaywallManagerBridgeModule(context);
30
30
  case NamiPurchaseManagerBridgeModule.NAME ->
31
31
  new NamiPurchaseManagerBridgeModule(context);
32
+ case NamiOverlayControlBridgeModule.NAME ->
33
+ new NamiOverlayControlBridgeModule(context);
32
34
  default -> null;
33
35
  };
34
36
  }
@@ -129,6 +131,19 @@ public class NamiBridgePackage extends TurboReactPackage {
129
131
  BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
130
132
  )
131
133
  );
134
+
135
+ moduleInfos.put(
136
+ NamiOverlayControlBridgeModule.NAME,
137
+ new ReactModuleInfo(
138
+ NamiOverlayControlBridgeModule.NAME,
139
+ NamiOverlayControlBridgeModule.NAME,
140
+ false,
141
+ false,
142
+ true,
143
+ false,
144
+ BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
145
+ )
146
+ );
132
147
  return moduleInfos;
133
148
  };
134
149
  }
@@ -4,39 +4,40 @@ import android.os.Handler
4
4
  import android.os.Looper
5
5
  import android.util.Log
6
6
  import com.facebook.react.bridge.*
7
- import com.facebook.react.modules.core.DeviceEventManagerModule
8
7
  import com.facebook.react.module.annotations.ReactModule
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
9
9
  import com.facebook.react.turbomodule.core.interfaces.TurboModule
10
10
  import com.namiml.flow.NamiFlowManager
11
11
 
12
12
  @ReactModule(name = NamiFlowManagerBridgeModule.NAME)
13
13
  class NamiFlowManagerBridgeModule internal constructor(
14
- reactContext: ReactApplicationContext
15
- ) : ReactContextBaseJavaModule(reactContext), TurboModule {
16
-
14
+ reactContext: ReactApplicationContext,
15
+ ) : ReactContextBaseJavaModule(reactContext),
16
+ TurboModule {
17
17
  companion object {
18
18
  const val NAME = "RNNamiFlowManager"
19
19
  }
20
20
 
21
- override fun getName(): String {
22
- return NAME
23
- }
21
+ override fun getName(): String = NAME
24
22
 
25
23
  @ReactMethod
26
24
  fun registerStepHandoff() {
27
25
  NamiFlowManager.registerStepHandoff { handoffTag, handoffData ->
28
- val payload = Arguments.createMap().apply {
29
- putString("handoffTag", handoffTag)
30
- if (handoffData != null) {
31
- try {
32
- val map = Arguments.makeNativeMap(handoffData)
33
- putMap("handoffData", map)
34
- } catch (e: Exception) {
35
- Log.d(NAME, "Failed to convert handoffData to NativeMap: ${e.localizedMessage}")
26
+ val payload =
27
+ Arguments.createMap().apply {
28
+ putString("handoffTag", handoffTag)
29
+ if (handoffData != null) {
30
+ try {
31
+ putMap("handoffData", Arguments.makeNativeMap(handoffData))
32
+ } catch (e: Exception) {
33
+ Log.d(NAME, "Failed to convert handoffData to NativeMap: ${e.localizedMessage}")
34
+ }
36
35
  }
37
36
  }
37
+
38
+ reactApplicationContext.runOnUiQueueThread {
39
+ sendEvent("Handoff", payload)
38
40
  }
39
- sendEvent("Handoff", payload)
40
41
  }
41
42
  }
42
43
 
@@ -74,7 +75,10 @@ class NamiFlowManagerBridgeModule internal constructor(
74
75
  promise.resolve(NamiFlowManager.isFlowOpen())
75
76
  }
76
77
 
77
- private fun sendEvent(eventName: String, params: WritableMap?) {
78
+ private fun sendEvent(
79
+ eventName: String,
80
+ params: WritableMap?,
81
+ ) {
78
82
  reactApplicationContext
79
83
  .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
80
84
  .emit(eventName, params)
@@ -82,5 +86,6 @@ class NamiFlowManagerBridgeModule internal constructor(
82
86
 
83
87
  // Required for RN EventEmitter support
84
88
  @ReactMethod fun addListener(eventName: String?) {}
89
+
85
90
  @ReactMethod fun removeListeners(count: Int?) {}
86
91
  }
@@ -0,0 +1,171 @@
1
+ package com.namiml.reactnative
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import android.os.Build
6
+ import android.util.Log
7
+ import com.facebook.react.bridge.*
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
9
+ import com.facebook.react.module.annotations.ReactModule
10
+ import com.facebook.react.turbomodule.core.interfaces.TurboModule
11
+ import com.facebook.react.bridge.UiThreadUtil
12
+
13
+ @ReactModule(name = NamiOverlayControlBridgeModule.NAME)
14
+ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
15
+ : ReactContextBaseJavaModule(ctx), TurboModule {
16
+
17
+ companion object {
18
+ const val NAME = "RNNamiOverlayControl"
19
+ var currentOverlayActivity: ReactOverlayActivity? = null
20
+ var lastValidActivity: Activity? = null
21
+ }
22
+
23
+ override fun getName() = NAME
24
+
25
+ @ReactMethod
26
+ fun presentOverlay(promise: Promise) {
27
+ var theActivity: Activity? = null
28
+ if (reactApplicationContext.hasCurrentActivity()) {
29
+ theActivity = reactApplicationContext.currentActivity
30
+
31
+ // Check if it's the overlay activity that's finishing
32
+ if (theActivity is ReactOverlayActivity) {
33
+ if (theActivity.isFinishing || theActivity.isDestroyed) {
34
+ theActivity = lastValidActivity
35
+ if (theActivity == null || theActivity.isFinishing || theActivity.isDestroyed) {
36
+ promise.reject("NO_ACTIVITY", "No valid activity available")
37
+ return
38
+ }
39
+ } else {
40
+ lastValidActivity = theActivity
41
+ }
42
+ } else {
43
+ lastValidActivity = theActivity
44
+ }
45
+ } else {
46
+ theActivity = lastValidActivity
47
+ if (theActivity == null || theActivity.isFinishing || theActivity.isDestroyed) {
48
+ promise.reject("NO_ACTIVITY", "No valid activity available")
49
+ return
50
+ }
51
+ }
52
+
53
+ if (theActivity == null) {
54
+ promise.reject("NO_ACTIVITY", "No activity available")
55
+ return
56
+ }
57
+
58
+ startOverlayActivity(theActivity, promise)
59
+ }
60
+
61
+ private fun startOverlayActivity(activity: Activity, promise: Promise) {
62
+ try {
63
+ UiThreadUtil.runOnUiThread {
64
+ try {
65
+ val intent = Intent(activity, ReactOverlayActivity::class.java)
66
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
67
+ activity.startActivity(intent)
68
+ // Use modern transition API for Android 14+ (API 34+)
69
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
70
+ activity.overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, 0, 0)
71
+ } else {
72
+ @Suppress("DEPRECATION")
73
+ activity.overridePendingTransition(0, 0)
74
+ }
75
+
76
+ // Emit ready event after a short delay to ensure activity is started
77
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
78
+ try {
79
+ if (ctx.catalystInstance != null) {
80
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
81
+ .emit("NamiOverlayReady", null)
82
+ }
83
+ } catch (e: Exception) {
84
+ Log.e(NAME, "Failed to emit NamiOverlayReady: ${e.message}")
85
+ }
86
+ }, 100)
87
+
88
+ promise.resolve(null)
89
+ } catch (uiError: Exception) {
90
+ Log.e(NAME, "Error in UI thread: ${uiError.message}", uiError)
91
+ promise.reject("UI_THREAD_ERROR", "Failed in UI thread: ${uiError.message}", uiError)
92
+ }
93
+ }
94
+ } catch (e: Exception) {
95
+ Log.e(NAME, "Error presenting overlay: ${e.message}", e)
96
+ promise.reject("PRESENT_OVERLAY_ERROR", "Failed to present overlay: ${e.message}", e)
97
+ }
98
+ }
99
+
100
+ @ReactMethod
101
+ fun addListener(eventName: String?) {
102
+ // Required for NativeEventEmitter - no-op since we emit events directly
103
+ }
104
+
105
+ @ReactMethod
106
+ fun removeListeners(count: Int?) {
107
+ // Required for NativeEventEmitter - no-op since we emit events directly
108
+ }
109
+
110
+ @ReactMethod
111
+ fun finishOverlay(result: ReadableMap?, promise: Promise) {
112
+ try {
113
+ // Emit result to main app listeners
114
+ val payload: WritableMap = Arguments.createMap().apply {
115
+ if (result != null) {
116
+ merge(result)
117
+ }
118
+ }
119
+ try {
120
+ if (ctx.catalystInstance != null) {
121
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
122
+ .emit("NamiOverlayResult", payload)
123
+ }
124
+ } catch (e: Exception) {
125
+ Log.e(NAME, "Failed to emit NamiOverlayResult: ${e.message}")
126
+ }
127
+
128
+ UiThreadUtil.runOnUiThread {
129
+ // Try to finish the tracked overlay activity first
130
+ currentOverlayActivity?.let { overlay ->
131
+ overlay.finish()
132
+ // Use modern transition API for Android 14+ (API 34+)
133
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
134
+ overlay.overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, 0, 0)
135
+ } else {
136
+ @Suppress("DEPRECATION")
137
+ overlay.overridePendingTransition(0, 0)
138
+ }
139
+ currentOverlayActivity = null
140
+
141
+ // Wait for activity to actually finish before resolving promise
142
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
143
+ promise.resolve(null)
144
+ }, 100)
145
+ } ?: run {
146
+ // Fallback to current activity if it's an overlay
147
+ (ctx.currentActivity as? ReactOverlayActivity)?.apply {
148
+ finish()
149
+ // Use modern transition API for Android 14+ (API 34+)
150
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
151
+ overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, 0, 0)
152
+ } else {
153
+ @Suppress("DEPRECATION")
154
+ overridePendingTransition(0, 0)
155
+ }
156
+
157
+ // Wait for activity to actually finish before resolving promise
158
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
159
+ promise.resolve(null)
160
+ }, 100)
161
+ } ?: run {
162
+ promise.resolve(null)
163
+ }
164
+ }
165
+ }
166
+ } catch (e: Exception) {
167
+ Log.e(NAME, "Failed to finish overlay: ${e.message}", e)
168
+ promise.reject("FINISH_OVERLAY_ERROR", "Failed to finish overlay", e)
169
+ }
170
+ }
171
+ }
@@ -0,0 +1,29 @@
1
+ package com.namiml.reactnative
2
+
3
+ import android.os.Bundle
4
+ import com.facebook.react.ReactActivity
5
+
6
+ class ReactOverlayActivity : ReactActivity() {
7
+ override fun getMainComponentName(): String = "NamiOverlayHost"
8
+
9
+ override fun onCreate(savedInstanceState: Bundle?) {
10
+ super.onCreate(savedInstanceState)
11
+ overridePendingTransition(0, 0)
12
+
13
+ // Register this activity with the bridge module
14
+ NamiOverlayControlBridgeModule.currentOverlayActivity = this
15
+ }
16
+
17
+ override fun onBackPressed() {
18
+ finish()
19
+ overridePendingTransition(0, 0)
20
+ }
21
+
22
+ override fun onDestroy() {
23
+ super.onDestroy()
24
+ // Clear the reference when activity is destroyed
25
+ if (NamiOverlayControlBridgeModule.currentOverlayActivity == this) {
26
+ NamiOverlayControlBridgeModule.currentOverlayActivity = null
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <style name="Theme.TransparentReact" parent="Theme.MaterialComponents.DayNight.NoActionBar">
4
+ <item name="android:windowIsTranslucent">true</item>
5
+ <item name="android:windowBackground">@android:color/transparent</item>
6
+ <item name="android:windowNoTitle">true</item>
7
+ <item name="android:statusBarColor">@android:color/transparent</item>
8
+ <item name="android:navigationBarColor">@android:color/transparent</item>
9
+ </style>
10
+ </resources>
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export { NamiEntitlementManager } from './src/NamiEntitlementManager';
5
5
  export { NamiPurchaseManager } from './src/NamiPurchaseManager';
6
6
  export { NamiPaywallManager } from './src/NamiPaywallManager';
7
7
  export { NamiFlowManager } from './src/NamiFlowManager';
8
+ export { NamiOverlayControl } from './src/NamiOverlayControl';
8
9
  export * from './src/types';
@@ -0,0 +1,9 @@
1
+ import type { TurboModule } from 'react-native';
2
+ export interface Spec extends TurboModule {
3
+ presentOverlay(): Promise<void>;
4
+ finishOverlay(result?: {
5
+ [key: string]: unknown;
6
+ } | null): Promise<void>;
7
+ }
8
+ declare const _default: Spec;
9
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import { NativeEventEmitter } from 'react-native';
2
+ export declare const NamiOverlayControl: {
3
+ emitter: NativeEventEmitter;
4
+ presentOverlay(): Promise<void>;
5
+ finishOverlay(result?: any): Promise<void>;
6
+ onOverlayReady(handler: () => void): () => void;
7
+ onOverlayResult(handler: (result: any) => void): () => void;
8
+ };
9
+ export default function NamiOverlayHost(): any;
@@ -0,0 +1 @@
1
+ export default function NamiOverlayHost(): null;
@@ -0,0 +1,4 @@
1
+ export declare function presentOverlay(): Promise<void>;
2
+ export declare function finishOverlay(result?: any): Promise<void>;
3
+ export declare function onOverlayReady(handler: () => void): () => void;
4
+ export declare function onOverlayResult(handler: (result: any) => void): () => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -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.2-3";
5
+ export declare const NAMI_REACT_NATIVE_VERSION = "3.3.2-5";
package/index.ts CHANGED
@@ -5,4 +5,5 @@ export { NamiEntitlementManager } from './src/NamiEntitlementManager';
5
5
  export { NamiPurchaseManager } from './src/NamiPurchaseManager';
6
6
  export { NamiPaywallManager } from './src/NamiPaywallManager';
7
7
  export { NamiFlowManager } from './src/NamiFlowManager';
8
+ export { NamiOverlayControl } from './src/NamiOverlayControl';
8
9
  export * from './src/types';
package/ios/Nami.swift CHANGED
@@ -8,9 +8,9 @@ import Foundation
8
8
  import NamiApple
9
9
  import React
10
10
 
11
- // #if RCT_NEW_ARCH_ENABLED
12
- // extension RNNami: RCTTurboModule {}
13
- // #endif
11
+ #if RCT_NEW_ARCH_ENABLED
12
+ extension RNNami: RCTTurboModule {}
13
+ #endif
14
14
 
15
15
  @objc(RNNami)
16
16
  class RNNami: NSObject {
@@ -21,7 +21,7 @@ RCT_EXTERN_METHOD(refresh:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRe
21
21
  RCT_EXTERN_METHOD(registerAvailableCampaignsHandler)
22
22
 
23
23
  + (BOOL)requiresMainQueueSetup {
24
- return NO;
24
+ return YES;
25
25
  }
26
26
 
27
27
  @end
@@ -10,9 +10,9 @@ import NamiApple
10
10
  import os
11
11
  import React
12
12
 
13
- // #if RCT_NEW_ARCH_ENABLED
14
- // extension RNNamiCampaignManager: RCTTurboModule {}
15
- // #endif
13
+ #if RCT_NEW_ARCH_ENABLED
14
+ extension RNNamiCampaignManager: RCTTurboModule {}
15
+ #endif
16
16
 
17
17
  @objc(RNNamiCampaignManager)
18
18
  class RNNamiCampaignManager: RCTEventEmitter {
@@ -20,7 +20,20 @@ class RNNamiCampaignManager: RCTEventEmitter {
20
20
 
21
21
  override init() {
22
22
  super.init()
23
- RNNamiCampaignManager.shared = self
23
+ }
24
+
25
+ override class func requiresMainQueueSetup() -> Bool { true }
26
+
27
+ private var hasListeners = false
28
+ override func startObserving() { hasListeners = true }
29
+ override func stopObserving() { hasListeners = false }
30
+
31
+ private func safeSend(withName name: String, body: Any?) {
32
+ guard hasListeners else {
33
+ print("[RNNamiCampaignManager] Warning: no listeners, so event not being sent to JS.")
34
+ return
35
+ } // optional but avoids warnings
36
+ sendEvent(withName: name, body: body)
24
37
  }
25
38
 
26
39
  override func supportedEvents() -> [String]! {
@@ -138,9 +151,7 @@ class RNNamiCampaignManager: RCTEventEmitter {
138
151
  "timeSpentOnPaywall": paywallEvent.timeSpentOnPaywall,
139
152
  ]
140
153
 
141
- DispatchQueue.main.async {
142
- RNNamiCampaignManager.shared?.sendEvent(withName: "NamiPaywallEvent", body: payload)
143
- }
154
+ safeSend(withName: "NamiPaywallEvent", body: payload)
144
155
  }
145
156
 
146
157
  func handleLaunch(callback: RCTResponseSenderBlock?, success: Bool, error: Error?) {
@@ -294,9 +305,7 @@ class RNNamiCampaignManager: RCTEventEmitter {
294
305
  func registerForAvailableCampaigns() {
295
306
  NamiCampaignManager.registerAvailableCampaignsHandler { availableCampaigns in
296
307
  let dictionaries = availableCampaigns.map { campaign in self.campaignInToDictionary(campaign) }
297
- DispatchQueue.main.async {
298
- RNNamiCampaignManager.shared?.sendEvent(withName: "AvailableCampaignsChanged", body: dictionaries)
299
- }
308
+ self.safeSend(withName: "AvailableCampaignsChanged", body: dictionaries)
300
309
  }
301
310
  }
302
311
  }
@@ -44,7 +44,7 @@ RCT_EXTERN_METHOD(registerAccountStateHandler)
44
44
 
45
45
 
46
46
  + (BOOL)requiresMainQueueSetup {
47
- return NO;
47
+ return YES;
48
48
  }
49
49
 
50
50
  @end
@@ -9,9 +9,9 @@ import Foundation
9
9
  import NamiApple
10
10
  import React
11
11
 
12
- // #if RCT_NEW_ARCH_ENABLED
13
- // extension RNNamiCustomerManager: RCTTurboModule {}
14
- // #endif
12
+ #if RCT_NEW_ARCH_ENABLED
13
+ extension RNNamiCustomerManager: RCTTurboModule {}
14
+ #endif
15
15
 
16
16
  @objc(RNNamiCustomerManager)
17
17
  class RNNamiCustomerManager: RCTEventEmitter {
@@ -19,7 +19,20 @@ class RNNamiCustomerManager: RCTEventEmitter {
19
19
 
20
20
  override init() {
21
21
  super.init()
22
- RNNamiCustomerManager.shared = self
22
+ }
23
+
24
+ override class func requiresMainQueueSetup() -> Bool { true }
25
+
26
+ private var hasListeners = false
27
+ override func startObserving() { hasListeners = true }
28
+ override func stopObserving() { hasListeners = false }
29
+
30
+ private func safeSend(withName name: String, body: Any?) {
31
+ guard hasListeners else {
32
+ print("[RNNamiCustomerManager] Warning: no listeners, so event not being sent to JS.")
33
+ return
34
+ } // optional but avoids warnings
35
+ sendEvent(withName: name, body: body)
23
36
  }
24
37
 
25
38
  override func supportedEvents() -> [String]! {
@@ -141,9 +154,7 @@ class RNNamiCustomerManager: RCTEventEmitter {
141
154
  func registerJourneyStateHandler() {
142
155
  NamiCustomerManager.registerJourneyStateHandler { journeyState in
143
156
  let dictionary = self.journeyStateToDictionary(journeyState)
144
- DispatchQueue.main.async {
145
- RNNamiCustomerManager.shared?.sendEvent(withName: "JourneyStateChanged", body: dictionary)
146
- }
157
+ self.safeSend(withName: "JourneyStateChanged", body: dictionary)
147
158
  }
148
159
  }
149
160
 
@@ -184,9 +195,7 @@ class RNNamiCustomerManager: RCTEventEmitter {
184
195
  "success": success,
185
196
  "error": error?._code as Any,
186
197
  ]
187
- DispatchQueue.main.async {
188
- RNNamiCustomerManager.shared?.sendEvent(withName: "AccountStateChanged", body: payload)
189
- }
198
+ self.safeSend(withName: "AccountStateChanged", body: payload)
190
199
  }
191
200
  }
192
201
  }
@@ -21,7 +21,7 @@ RCT_EXTERN_METHOD(registerActiveEntitlementsHandler)
21
21
  RCT_EXTERN_METHOD(clearProvisionalEntitlementGrants)
22
22
 
23
23
  + (BOOL)requiresMainQueueSetup {
24
- return NO;
24
+ return YES;
25
25
  }
26
26
 
27
27
  @end
@@ -9,9 +9,9 @@ import Foundation
9
9
  import NamiApple
10
10
  import React
11
11
 
12
- // #if RCT_NEW_ARCH_ENABLED
13
- // extension RNNamiEntitlementManager: RCTTurboModule {}
14
- // #endif
12
+ #if RCT_NEW_ARCH_ENABLED
13
+ extension RNNamiEntitlementManager: RCTTurboModule {}
14
+ #endif
15
15
 
16
16
  @objc(RNNamiEntitlementManager)
17
17
  class RNNamiEntitlementManager: RCTEventEmitter {
@@ -22,8 +22,18 @@ class RNNamiEntitlementManager: RCTEventEmitter {
22
22
  RNNamiEntitlementManager.shared = self
23
23
  }
24
24
 
25
- override static func requiresMainQueueSetup() -> Bool {
26
- return false
25
+ override class func requiresMainQueueSetup() -> Bool { true }
26
+
27
+ private var hasListeners = false
28
+ override func startObserving() { hasListeners = true }
29
+ override func stopObserving() { hasListeners = false }
30
+
31
+ private func safeSend(withName name: String, body: Any?) {
32
+ guard hasListeners else {
33
+ print("[RNNamiEntitlementManager] Warning: no listeners, so event not being sent to JS.")
34
+ return
35
+ }
36
+ sendEvent(withName: name, body: body)
27
37
  }
28
38
 
29
39
  override func supportedEvents() -> [String]! {
@@ -74,9 +84,7 @@ class RNNamiEntitlementManager: RCTEventEmitter {
74
84
  func refresh() {
75
85
  NamiEntitlementManager.refresh { entitlements in
76
86
  let dicts = entitlements.map { self.entitlementToDictionary($0) }
77
- DispatchQueue.main.async {
78
- RNNamiEntitlementManager.shared?.sendEvent(withName: "EntitlementsChanged", body: dicts)
79
- }
87
+ self.safeSend(withName: "EntitlementsChanged", body: dicts)
80
88
  }
81
89
  }
82
90
 
@@ -84,9 +92,7 @@ class RNNamiEntitlementManager: RCTEventEmitter {
84
92
  func registerActiveEntitlementsHandler() {
85
93
  NamiEntitlementManager.registerActiveEntitlementsHandler { entitlements in
86
94
  let dicts = entitlements.map { self.entitlementToDictionary($0) }
87
- DispatchQueue.main.async {
88
- RNNamiEntitlementManager.shared?.sendEvent(withName: "EntitlementsChanged", body: dicts)
89
- }
95
+ self.safeSend(withName: "EntitlementsChanged", body: dicts)
90
96
  }
91
97
  }
92
98
 
@@ -17,7 +17,7 @@ RCT_EXTERN_METHOD(finish)
17
17
  RCT_EXTERN_METHOD(isFlowOpen:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
18
18
 
19
19
  + (BOOL)requiresMainQueueSetup {
20
- return NO;
20
+ return YES;
21
21
  }
22
22
 
23
23
  @end
@@ -9,9 +9,9 @@ import Foundation
9
9
  import NamiApple
10
10
  import React
11
11
 
12
- // #if RCT_NEW_ARCH_ENABLED
13
- // extension RNNamiFlowManager: RCTTurboModule {}
14
- // #endif
12
+ #if RCT_NEW_ARCH_ENABLED
13
+ extension RNNamiFlowManager: RCTTurboModule {}
14
+ #endif
15
15
 
16
16
  @objc(RNNamiFlowManager)
17
17
  class RNNamiFlowManager: RCTEventEmitter {
@@ -19,11 +19,20 @@ class RNNamiFlowManager: RCTEventEmitter {
19
19
 
20
20
  override init() {
21
21
  super.init()
22
- RNNamiFlowManager.shared = self
23
22
  }
24
23
 
25
- override static func requiresMainQueueSetup() -> Bool {
26
- return false
24
+ override class func requiresMainQueueSetup() -> Bool { true }
25
+
26
+ private var hasListeners = false
27
+ override func startObserving() { hasListeners = true }
28
+ override func stopObserving() { hasListeners = false }
29
+
30
+ private func safeSend(withName name: String, body: Any?) {
31
+ guard hasListeners else {
32
+ print("[RNNamiFlowManager] Warning: no listeners, so event not being sent to JS.")
33
+ return
34
+ }
35
+ sendEvent(withName: name, body: body)
27
36
  }
28
37
 
29
38
  override func supportedEvents() -> [String]! {
@@ -39,36 +48,26 @@ class RNNamiFlowManager: RCTEventEmitter {
39
48
  payload["handoffData"] = data
40
49
  }
41
50
 
42
- DispatchQueue.main.async {
43
- RNNamiFlowManager.shared?.sendEvent(withName: "Handoff", body: payload)
44
- }
51
+ self.safeSend(withName: "Handoff", body: payload)
45
52
  }
46
53
  }
47
54
 
48
55
  @objc func registerEventHandler() {
49
56
  NamiFlowManager.registerEventHandler { payload in
50
- DispatchQueue.main.async {
51
- RNNamiFlowManager.shared?.sendEvent(withName: "FlowEvent", body: payload)
52
- }
57
+ self.safeSend(withName: "FlowEvent", body: payload)
53
58
  }
54
59
  }
55
60
 
56
61
  @objc func resume() {
57
- DispatchQueue.main.async {
58
- NamiFlowManager.resume()
59
- }
62
+ NamiFlowManager.resume()
60
63
  }
61
64
 
62
65
  @objc func pause() {
63
- DispatchQueue.main.async {
64
- NamiFlowManager.pause()
65
- }
66
+ NamiFlowManager.pause()
66
67
  }
67
68
 
68
69
  @objc func finish() {
69
- DispatchQueue.main.async {
70
- NamiFlowManager.finish()
71
- }
70
+ NamiFlowManager.finish()
72
71
  }
73
72
 
74
73
  @objc func isFlowOpen(_ resolve: @escaping RCTPromiseResolveBlock, rejecter _: @escaping RCTPromiseRejectBlock) {
@@ -0,0 +1,17 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(RNNamiOverlayControl, RCTEventEmitter)
5
+
6
+ RCT_EXTERN_METHOD(presentOverlay:(RCTPromiseResolveBlock)resolve
7
+ rejecter:(RCTPromiseRejectBlock)reject)
8
+
9
+ RCT_EXTERN_METHOD(finishOverlay:(NSDictionary *)result
10
+ resolver:(RCTPromiseResolveBlock)resolve
11
+ rejecter:(RCTPromiseRejectBlock)reject)
12
+
13
+ + (BOOL)requiresMainQueueSetup {
14
+ return YES;
15
+ }
16
+
17
+ @end
@@ -0,0 +1,136 @@
1
+ import Foundation
2
+ import React
3
+ import UIKit
4
+
5
+ #if RCT_NEW_ARCH_ENABLED
6
+ extension RNNamiPaywallManager: RCTTurboModule {}
7
+ #endif
8
+
9
+ @objc(RNNamiOverlayControl)
10
+ class NamiOverlayControlBridge: RCTEventEmitter {
11
+ private var overlayViewController: UIViewController?
12
+ private var hasListeners = false
13
+ private var isPresenting = false
14
+ private var isDismissing = false
15
+
16
+ override static func requiresMainQueueSetup() -> Bool {
17
+ return true
18
+ }
19
+
20
+ override func supportedEvents() -> [String]! {
21
+ return ["NamiOverlayReady", "NamiOverlayResult"]
22
+ }
23
+
24
+ override func startObserving() { hasListeners = true }
25
+ override func stopObserving() { hasListeners = false }
26
+
27
+ private func safeSend(withName name: String, body: Any?) {
28
+ guard hasListeners else {
29
+ print("[RNNamiOverlayControl] Warning: no listeners, so event not being sent to JS.")
30
+ return
31
+ }
32
+ sendEvent(withName: name, body: body)
33
+ }
34
+
35
+ @objc func presentOverlay(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
36
+ DispatchQueue.main.async {
37
+ // If we're already presenting or dismissing, wait and retry
38
+ if self.isPresenting || self.isDismissing {
39
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
40
+ self.presentOverlay(resolve, rejecter: reject)
41
+ }
42
+ return
43
+ }
44
+
45
+ // Find the root view controller more reliably
46
+ var rootViewController: UIViewController?
47
+
48
+ if #available(iOS 13.0, *) {
49
+ rootViewController = UIApplication.shared.connectedScenes
50
+ .compactMap { $0 as? UIWindowScene }
51
+ .flatMap { $0.windows }
52
+ .first(where: { $0.isKeyWindow })?.rootViewController
53
+ } else {
54
+ rootViewController = UIApplication.shared.keyWindow?.rootViewController
55
+ }
56
+
57
+ guard let rootVC = rootViewController else {
58
+ reject("NO_ROOT_VIEW_CONTROLLER", "No root view controller available", nil)
59
+ return
60
+ }
61
+
62
+ // Create a React Native view controller for the overlay
63
+ guard let bridge = self.bridge else {
64
+ reject("NO_BRIDGE", "React Native bridge not available", nil)
65
+ return
66
+ }
67
+
68
+ let rootView = RCTRootView(
69
+ bridge: bridge,
70
+ moduleName: "NamiOverlayHost",
71
+ initialProperties: [:]
72
+ )
73
+
74
+ let overlayViewController = UIViewController()
75
+ overlayViewController.view = rootView
76
+ overlayViewController.modalPresentationStyle = .overFullScreen
77
+ overlayViewController.modalTransitionStyle = .crossDissolve
78
+ overlayViewController.view.backgroundColor = UIColor.clear
79
+
80
+ self.overlayViewController = overlayViewController
81
+
82
+ // Find the top-most presented view controller
83
+ var topController = rootVC
84
+ while let presented = topController.presentedViewController {
85
+ topController = presented
86
+ }
87
+
88
+ // Check if the top controller can present (not already in the process of presenting/dismissing)
89
+ if topController.isBeingPresented || topController.isBeingDismissed {
90
+ // Wait longer and try again
91
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
92
+ self.presentOverlay(resolve, rejecter: reject)
93
+ }
94
+ return
95
+ }
96
+
97
+ self.isPresenting = true
98
+
99
+ topController.present(overlayViewController, animated: false) {
100
+ self.isPresenting = false
101
+ // Emit ready event after presentation completes
102
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
103
+ if self.hasListeners {
104
+ self.safeSend(withName: "NamiOverlayReady", body: nil)
105
+ }
106
+ }
107
+ resolve(nil)
108
+ }
109
+ }
110
+ }
111
+
112
+ @objc func finishOverlay(_ result: NSDictionary?, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter _: @escaping RCTPromiseRejectBlock) {
113
+ DispatchQueue.main.async {
114
+ guard let overlayViewController = self.overlayViewController else {
115
+ resolve(nil)
116
+ return
117
+ }
118
+
119
+ // Emit result to listeners
120
+ if self.hasListeners {
121
+ self.safeSend(withName: "NamiOverlayResult", body: result)
122
+ }
123
+
124
+ self.isDismissing = true
125
+
126
+ overlayViewController.dismiss(animated: false) {
127
+ self.overlayViewController = nil
128
+ self.isDismissing = false
129
+ // Add a longer delay before resolving to ensure the view controller is fully dismissed
130
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
131
+ resolve(nil)
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
@@ -46,7 +46,7 @@ RCT_EXTERN_METHOD(setAppSuppliedVideoDetails:(NSString *)url name:(nullable NSSt
46
46
  RCT_EXTERN_METHOD(allowUserInteraction:(BOOL *)allowed)
47
47
 
48
48
  + (BOOL)requiresMainQueueSetup {
49
- return NO;
49
+ return YES;
50
50
  }
51
51
 
52
52
  @end
@@ -9,9 +9,9 @@ import Foundation
9
9
  import NamiApple
10
10
  import React
11
11
 
12
- // #if RCT_NEW_ARCH_ENABLED
13
- // extension RNNamiPaywallManager: RCTTurboModule {}
14
- // #endif
12
+ #if RCT_NEW_ARCH_ENABLED
13
+ extension RNNamiPaywallManager: RCTTurboModule {}
14
+ #endif
15
15
 
16
16
  @objc(RNNamiPaywallManager)
17
17
  class RNNamiPaywallManager: RCTEventEmitter {
@@ -19,7 +19,20 @@ class RNNamiPaywallManager: RCTEventEmitter {
19
19
 
20
20
  override init() {
21
21
  super.init()
22
- RNNamiPaywallManager.shared = self
22
+ }
23
+
24
+ override class func requiresMainQueueSetup() -> Bool { true }
25
+
26
+ private var hasListeners = false
27
+ override func startObserving() { hasListeners = true }
28
+ override func stopObserving() { hasListeners = false }
29
+
30
+ private func safeSend(withName name: String, body: Any?) {
31
+ guard hasListeners else {
32
+ print("[RNNamiPaywallManager] Warning: no listeners, so event not being sent to JS.")
33
+ return
34
+ } // optional but avoids warnings
35
+ sendEvent(withName: name, body: body)
23
36
  }
24
37
 
25
38
  override func supportedEvents() -> [String]! {
@@ -80,9 +93,7 @@ class RNNamiPaywallManager: RCTEventEmitter {
80
93
  func registerBuySkuHandler() {
81
94
  NamiPaywallManager.registerBuySkuHandler { sku in
82
95
  let dictionary = RNNamiPurchaseManager.skuToSKUDict(sku)
83
- DispatchQueue.main.async {
84
- RNNamiPaywallManager.shared?.sendEvent(withName: "RegisterBuySKU", body: dictionary)
85
- }
96
+ self.safeSend(withName: "RegisterBuySKU", body: dictionary)
86
97
  }
87
98
  }
88
99
 
@@ -90,9 +101,7 @@ class RNNamiPaywallManager: RCTEventEmitter {
90
101
  func registerCloseHandler() {
91
102
  NamiPaywallManager.registerCloseHandler { _ in
92
103
  let dictionary = NSDictionary(dictionary: ["PaywallCloseRequested": true].compactMapValues { $0 })
93
- DispatchQueue.main.async {
94
- RNNamiPaywallManager.shared?.sendEvent(withName: "PaywallCloseRequested", body: dictionary)
95
- }
104
+ self.safeSend(withName: "PaywallCloseRequested", body: dictionary)
96
105
  }
97
106
  }
98
107
 
@@ -100,9 +109,7 @@ class RNNamiPaywallManager: RCTEventEmitter {
100
109
  func registerSignInHandler() {
101
110
  NamiPaywallManager.registerSignInHandler { _ in
102
111
  let dictionary = NSDictionary(dictionary: ["PaywallSignInRequested": true].compactMapValues { $0 })
103
- DispatchQueue.main.async {
104
- RNNamiPaywallManager.shared?.sendEvent(withName: "PaywallSignInRequested", body: dictionary)
105
- }
112
+ self.safeSend(withName: "PaywallSignInRequested", body: dictionary)
106
113
  }
107
114
  }
108
115
 
@@ -110,18 +117,14 @@ class RNNamiPaywallManager: RCTEventEmitter {
110
117
  func registerRestoreHandler() {
111
118
  NamiPaywallManager.registerRestoreHandler {
112
119
  let dictionary = NSDictionary(dictionary: ["PaywallRestoreRequested": true].compactMapValues { $0 })
113
- DispatchQueue.main.async {
114
- RNNamiPaywallManager.shared?.sendEvent(withName: "PaywallRestoreRequested", body: dictionary)
115
- }
120
+ self.safeSend(withName: "PaywallRestoreRequested", body: dictionary)
116
121
  }
117
122
  }
118
123
 
119
124
  @objc(registerDeeplinkActionHandler)
120
125
  func registerDeeplinkActionHandler() {
121
126
  NamiPaywallManager.registerDeeplinkActionHandler { url in
122
- DispatchQueue.main.async {
123
- RNNamiPaywallManager.shared?.sendEvent(withName: "PaywallDeeplinkAction", body: url)
124
- }
127
+ self.safeSend(withName: "PaywallDeeplinkAction", body: url)
125
128
  }
126
129
  }
127
130
 
@@ -30,7 +30,7 @@ RCT_EXTERN_METHOD(presentCodeRedemptionSheet)
30
30
  RCT_EXTERN_METHOD(restorePurchases)
31
31
 
32
32
  + (BOOL)requiresMainQueueSetup {
33
- return NO;
33
+ return YES;
34
34
  }
35
35
 
36
36
  @end
@@ -9,9 +9,9 @@ import Foundation
9
9
  import NamiApple
10
10
  import React
11
11
 
12
- // #if RCT_NEW_ARCH_ENABLED
13
- // extension RNNamiPurchaseManager: RCTTurboModule {}
14
- // #endif
12
+ #if RCT_NEW_ARCH_ENABLED
13
+ extension RNNamiPurchaseManager: RCTTurboModule {}
14
+ #endif
15
15
 
16
16
  @objc(RNNamiPurchaseManager)
17
17
  class RNNamiPurchaseManager: RCTEventEmitter {
@@ -19,7 +19,20 @@ class RNNamiPurchaseManager: RCTEventEmitter {
19
19
 
20
20
  override init() {
21
21
  super.init()
22
- RNNamiPurchaseManager.shared = self
22
+ }
23
+
24
+ override class func requiresMainQueueSetup() -> Bool { true }
25
+
26
+ private var hasListeners = false
27
+ override func startObserving() { hasListeners = true }
28
+ override func stopObserving() { hasListeners = false }
29
+
30
+ private func safeSend(withName name: String, body: Any?) {
31
+ guard hasListeners else {
32
+ print("[RNNamiPurchaseManager] Warning: no listeners, so event not being sent to JS.")
33
+ return
34
+ }
35
+ sendEvent(withName: name, body: body)
23
36
  }
24
37
 
25
38
  override func supportedEvents() -> [String]! {
@@ -158,7 +171,7 @@ class RNNamiPurchaseManager: RCTEventEmitter {
158
171
  "purchaseState": stateString,
159
172
  "error": error?.localizedDescription,
160
173
  ]
161
- self.sendEvent(withName: "PurchasesChanged", body: payload)
174
+ self.safeSend(withName: "PurchasesChanged", body: payload)
162
175
  }
163
176
  }
164
177
 
@@ -187,7 +200,7 @@ class RNNamiPurchaseManager: RCTEventEmitter {
187
200
  "newPurchases": newPurchasesDictionaries,
188
201
  "oldPurchases": oldPurchasesDictionaries,
189
202
  ]
190
- RNNamiPurchaseManager.shared?.sendEvent(withName: "RestorePurchasesStateChanged", body: payload)
203
+ self.safeSend(withName: "RestorePurchasesStateChanged", body: payload)
191
204
  }
192
205
  }
193
206
 
@@ -216,7 +229,7 @@ class RNNamiPurchaseManager: RCTEventEmitter {
216
229
  "newPurchases": newPurchasesDictionaries,
217
230
  "oldPurchases": oldPurchasesDictionaries,
218
231
  ]
219
- RNNamiPurchaseManager.shared?.sendEvent(withName: "RestorePurchasesStateChanged", body: payload)
232
+ self.safeSend(withName: "RestorePurchasesStateChanged", body: payload)
220
233
  }
221
234
  }
222
235
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nami-sdk",
3
- "version": "3.3.2-3",
3
+ "version": "3.3.2-5",
4
4
  "description": "React Native Module for Nami - Easy subscriptions & in-app purchases, with powerful built-in paywalls and A/B testing.",
5
5
  "main": "index.ts",
6
6
  "types": "dist/index.d.ts",
@@ -12,6 +12,13 @@
12
12
  "javaPackageName": "com.namiml.reactnative"
13
13
  }
14
14
  },
15
+ "react-native": {
16
+ "android": {
17
+ "sourceDir": "android",
18
+ "packageImportPath": "com.namiml.reactnative.NamiBridgePackage",
19
+ "libraryName": "react-native-nami-sdk"
20
+ }
21
+ },
15
22
  "scripts": {
16
23
  "build": "tsc",
17
24
  "generate:version": "ts-node scripts/generate-version.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.2.9'
24
+ s.dependency 'Nami', '3.3.2.10'
25
25
  s.dependency 'React'
26
26
 
27
27
  s.pod_target_xcconfig = {
@@ -0,0 +1,9 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ export interface Spec extends TurboModule {
5
+ presentOverlay(): Promise<void>;
6
+ finishOverlay(result?: { [key: string]: unknown } | null): Promise<void>;
7
+ }
8
+
9
+ export default TurboModuleRegistry.getEnforcing<Spec>('RNNamiOverlayControl');
@@ -0,0 +1,48 @@
1
+ import { AppRegistry, View, StyleSheet } from 'react-native';
2
+ import {
3
+ TurboModuleRegistry,
4
+ NativeModules,
5
+ NativeEventEmitter,
6
+ } from 'react-native';
7
+ import type { Spec } from '../specs/NativeNamiOverlayControl';
8
+
9
+ const RNNamiOverlayControl: Spec =
10
+ TurboModuleRegistry.getEnforcing?.<Spec>('RNNamiOverlayControl') ??
11
+ NativeModules.RNNamiOverlayControl;
12
+
13
+ const emitter = new NativeEventEmitter(NativeModules.RNNamiOverlayControl);
14
+
15
+ export const NamiOverlayControl = {
16
+ emitter,
17
+
18
+ presentOverlay(): Promise<void> {
19
+ return RNNamiOverlayControl.presentOverlay();
20
+ },
21
+
22
+ finishOverlay(result?: any): Promise<void> {
23
+ return RNNamiOverlayControl.finishOverlay(result ?? null);
24
+ },
25
+
26
+ onOverlayReady(handler: () => void) {
27
+ const sub = emitter.addListener('NamiOverlayReady', handler);
28
+ return () => sub.remove();
29
+ },
30
+
31
+ onOverlayResult(handler: (result: any) => void) {
32
+ const sub = emitter.addListener('NamiOverlayResult', handler);
33
+ return () => sub.remove();
34
+ },
35
+ };
36
+
37
+ export default function NamiOverlayHost() {
38
+ return <View style={styles.overlay} />;
39
+ }
40
+
41
+ const styles = StyleSheet.create({
42
+ overlay: {
43
+ flex: 1,
44
+ backgroundColor: 'transparent',
45
+ },
46
+ });
47
+
48
+ AppRegistry.registerComponent('NamiOverlayHost', () => NamiOverlayHost);
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.2-3';
5
+ export const NAMI_REACT_NATIVE_VERSION = '3.3.2-5';