react-native-levelplayads 0.1.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.
Files changed (70) hide show
  1. package/LICENSE +20 -0
  2. package/NitroLevelPlayAds.podspec +28 -0
  3. package/README.md +52 -0
  4. package/android/CMakeLists.txt +20 -0
  5. package/android/build.gradle +130 -0
  6. package/android/consumer-rules.pro +3 -0
  7. package/android/fix-prefab.gradle +46 -0
  8. package/android/gradle.properties +5 -0
  9. package/android/src/main/AndroidManifest.xml +1 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +7 -0
  11. package/android/src/main/java/com/margelo/nitro/levelplayads/HybridLevelPlayAds.kt +287 -0
  12. package/android/src/main/java/com/margelo/nitro/levelplayads/LevelPlayAndroidUtils.kt +30 -0
  13. package/android/src/main/java/com/margelo/nitro/levelplayads/NitroLevelPlayAdsPackage.java +27 -0
  14. package/ios/HybridLevelPlayAds.swift +274 -0
  15. package/ios/LevelPlayAdsSupport.swift +74 -0
  16. package/lib/module/index.js +31 -0
  17. package/lib/module/index.js.map +1 -0
  18. package/lib/module/index.test.js +78 -0
  19. package/lib/module/index.test.js.map +1 -0
  20. package/lib/module/package.json +1 -0
  21. package/lib/module/specs/LevelPlayAds.nitro.js +4 -0
  22. package/lib/module/specs/LevelPlayAds.nitro.js.map +1 -0
  23. package/lib/typescript/package.json +1 -0
  24. package/lib/typescript/src/index.d.ts +16 -0
  25. package/lib/typescript/src/index.d.ts.map +1 -0
  26. package/lib/typescript/src/index.test.d.ts +2 -0
  27. package/lib/typescript/src/index.test.d.ts.map +1 -0
  28. package/lib/typescript/src/specs/LevelPlayAds.nitro.d.ts +18 -0
  29. package/lib/typescript/src/specs/LevelPlayAds.nitro.d.ts.map +1 -0
  30. package/nitro.json +18 -0
  31. package/nitrogen/generated/android/NitroLevelPlayAds+autolinking.cmake +81 -0
  32. package/nitrogen/generated/android/NitroLevelPlayAds+autolinking.gradle +27 -0
  33. package/nitrogen/generated/android/NitroLevelPlayAdsOnLoad.cpp +44 -0
  34. package/nitrogen/generated/android/NitroLevelPlayAdsOnLoad.hpp +25 -0
  35. package/nitrogen/generated/android/c++/JHybridLevelPlayAdsSpec.cpp +112 -0
  36. package/nitrogen/generated/android/c++/JHybridLevelPlayAdsSpec.hpp +69 -0
  37. package/nitrogen/generated/android/c++/JLevelPlayAdShowResult.hpp +54 -0
  38. package/nitrogen/generated/android/c++/JLevelPlayAdShowState.hpp +59 -0
  39. package/nitrogen/generated/android/c++/JLevelPlayAdType.hpp +59 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/levelplayads/HybridLevelPlayAdsSpec.kt +72 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/levelplayads/LevelPlayAdShowResult.kt +29 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/levelplayads/LevelPlayAdShowState.kt +21 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/levelplayads/LevelPlayAdType.kt +21 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/levelplayads/NitroLevelPlayAdsOnLoad.kt +35 -0
  45. package/nitrogen/generated/ios/NitroLevelPlayAds+autolinking.rb +60 -0
  46. package/nitrogen/generated/ios/NitroLevelPlayAds-Swift-Cxx-Bridge.cpp +56 -0
  47. package/nitrogen/generated/ios/NitroLevelPlayAds-Swift-Cxx-Bridge.hpp +168 -0
  48. package/nitrogen/generated/ios/NitroLevelPlayAds-Swift-Cxx-Umbrella.hpp +54 -0
  49. package/nitrogen/generated/ios/NitroLevelPlayAdsAutolinking.mm +33 -0
  50. package/nitrogen/generated/ios/NitroLevelPlayAdsAutolinking.swift +25 -0
  51. package/nitrogen/generated/ios/c++/HybridLevelPlayAdsSpecSwift.cpp +11 -0
  52. package/nitrogen/generated/ios/c++/HybridLevelPlayAdsSpecSwift.hpp +116 -0
  53. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  54. package/nitrogen/generated/ios/swift/Func_void_LevelPlayAdShowResult.swift +47 -0
  55. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  56. package/nitrogen/generated/ios/swift/HybridLevelPlayAdsSpec.swift +54 -0
  57. package/nitrogen/generated/ios/swift/HybridLevelPlayAdsSpec_cxx.swift +201 -0
  58. package/nitrogen/generated/ios/swift/LevelPlayAdShowResult.swift +35 -0
  59. package/nitrogen/generated/ios/swift/LevelPlayAdShowState.swift +40 -0
  60. package/nitrogen/generated/ios/swift/LevelPlayAdType.swift +40 -0
  61. package/nitrogen/generated/shared/c++/HybridLevelPlayAdsSpec.cpp +26 -0
  62. package/nitrogen/generated/shared/c++/HybridLevelPlayAdsSpec.hpp +73 -0
  63. package/nitrogen/generated/shared/c++/LevelPlayAdShowResult.hpp +68 -0
  64. package/nitrogen/generated/shared/c++/LevelPlayAdShowState.hpp +76 -0
  65. package/nitrogen/generated/shared/c++/LevelPlayAdType.hpp +76 -0
  66. package/package.json +170 -0
  67. package/react-native.config.js +8 -0
  68. package/src/index.test.ts +119 -0
  69. package/src/index.ts +71 -0
  70. package/src/specs/LevelPlayAds.nitro.ts +26 -0
@@ -0,0 +1,274 @@
1
+ import Foundation
2
+ import NitroModules
3
+ // Verified by compiling a throwaway Swift file (`swiftc -typecheck`) against the
4
+ // real IronSourceSDK 9.5.0.0 XCFramework (https://trunk.cocoapods.org, pod
5
+ // "IronSourceSDK") — the framework module is named `IronSource`, and the unified
6
+ // LevelPlay iOS API lives under the `LPM*` prefix (LPMInterstitialAd, LPMRewardedAd,
7
+ // LPMInitRequestBuilder, LPMPrivacySettings, ...), distinct from the legacy
8
+ // `IronSourceAds` facade.
9
+ @preconcurrency import IronSource
10
+
11
+ final class HybridLevelPlayAds: HybridLevelPlayAdsSpec, @unchecked Sendable {
12
+ private let stateLock = NSLock()
13
+ private var isInitialized = false
14
+ private var isInitializing = false
15
+ private var initializedAppKey: String?
16
+ private var initializingAppKey: String?
17
+ private var initializationContinuations: [CheckedContinuation<Void, Error>] = []
18
+
19
+ // Keyed by adUnitId: load() and show() are separate calls in this module's
20
+ // spec, so the loaded ad object has to be held onto between the two calls.
21
+ private var loadedInterstitials: [String: InterstitialAdHolder] = [:]
22
+ private var loadedRewarded: [String: RewardedAdHolder] = [:]
23
+
24
+ // NSLock.lock()/unlock() are `noasync` in Swift 6 — this keeps locking out of
25
+ // `async` function bodies (a suspension point could otherwise occur while held).
26
+ private func withStateLock<T>(_ body: () -> T) -> T {
27
+ stateLock.lock()
28
+ defer { stateLock.unlock() }
29
+ return body()
30
+ }
31
+
32
+ func initialize(appKey: String, testMode: Bool) throws -> Promise<Void> {
33
+ guard !appKey.isEmpty else {
34
+ throw levelPlayAdsError("LevelPlay app key is empty")
35
+ }
36
+
37
+ return Promise.async {
38
+ try await withCheckedThrowingContinuation { continuation in
39
+ DispatchQueue.main.async {
40
+ self.stateLock.lock()
41
+ if self.isInitialized {
42
+ let existing = self.initializedAppKey
43
+ self.stateLock.unlock()
44
+ if let existing, existing != appKey {
45
+ continuation.resume(
46
+ throwing: levelPlayAdsError("LevelPlay SDK was already initialized with another app key")
47
+ )
48
+ } else {
49
+ continuation.resume()
50
+ }
51
+ return
52
+ }
53
+
54
+ if self.isInitializing, self.initializingAppKey != appKey {
55
+ self.stateLock.unlock()
56
+ continuation.resume(
57
+ throwing: levelPlayAdsError("LevelPlay SDK is being initialized with another app key")
58
+ )
59
+ return
60
+ }
61
+
62
+ self.initializationContinuations.append(continuation)
63
+ if self.isInitializing {
64
+ self.stateLock.unlock()
65
+ return
66
+ }
67
+ self.isInitializing = true
68
+ self.initializingAppKey = appKey
69
+ self.stateLock.unlock()
70
+
71
+ if testMode {
72
+ LevelPlay.setAdaptersDebug(true)
73
+ }
74
+
75
+ let request = LPMInitRequestBuilder(appKey: appKey).build()
76
+ LevelPlay.initWithRequest(request) { _, error in
77
+ self.handleInitialization(error: error)
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ fileprivate func handleInitialization(error: Error?) {
85
+ stateLock.lock()
86
+ let continuations = initializationContinuations
87
+ initializationContinuations.removeAll()
88
+ isInitializing = false
89
+ let appKey = initializingAppKey
90
+ initializingAppKey = nil
91
+ if error == nil {
92
+ isInitialized = true
93
+ initializedAppKey = appKey
94
+ }
95
+ stateLock.unlock()
96
+
97
+ continuations.forEach { continuation in
98
+ if let error {
99
+ continuation.resume(throwing: error)
100
+ } else {
101
+ continuation.resume()
102
+ }
103
+ }
104
+ }
105
+
106
+ func load(adType: LevelPlayAdType, adUnitId: String) throws -> Promise<Void> {
107
+ guard !adUnitId.isEmpty else {
108
+ throw levelPlayAdsError("LevelPlay ad unit ID is empty")
109
+ }
110
+
111
+ return Promise.async {
112
+ try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
113
+ switch adType {
114
+ case .interstitial:
115
+ DispatchQueue.main.async {
116
+ let holder = InterstitialAdHolder(adUnitId: adUnitId)
117
+ RetainBox.retain(holder)
118
+ holder.onLoaded = { [weak self] in
119
+ self?.withStateLock { self?.loadedInterstitials[adUnitId] = holder }
120
+ RetainBox.release(holder)
121
+ continuation.resume()
122
+ }
123
+ holder.onLoadFailed = { error in
124
+ RetainBox.release(holder)
125
+ continuation.resume(throwing: error)
126
+ }
127
+ holder.ad.loadAd()
128
+ }
129
+ case .rewarded:
130
+ DispatchQueue.main.async {
131
+ let holder = RewardedAdHolder(adUnitId: adUnitId)
132
+ RetainBox.retain(holder)
133
+ holder.onLoaded = { [weak self] in
134
+ self?.withStateLock { self?.loadedRewarded[adUnitId] = holder }
135
+ RetainBox.release(holder)
136
+ continuation.resume()
137
+ }
138
+ holder.onLoadFailed = { error in
139
+ RetainBox.release(holder)
140
+ continuation.resume(throwing: error)
141
+ }
142
+ holder.ad.loadAd()
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ func show(adType: LevelPlayAdType, adUnitId: String) throws -> Promise<LevelPlayAdShowResult> {
150
+ guard !adUnitId.isEmpty else {
151
+ throw levelPlayAdsError("LevelPlay ad unit ID is empty")
152
+ }
153
+
154
+ return Promise.async {
155
+ let viewController = try await currentViewController()
156
+
157
+ switch adType {
158
+ case .interstitial:
159
+ let holder = self.withStateLock { self.loadedInterstitials.removeValue(forKey: adUnitId) }
160
+ guard let holder else {
161
+ throw levelPlayAdsError("LevelPlay interstitial ad is not ready")
162
+ }
163
+ // Reuse the same delegate that was registered in load() — swapping in a
164
+ // different delegate instance here is what used to drop didCloseAd.
165
+ RetainBox.retain(holder)
166
+ return try await withCheckedThrowingContinuation {
167
+ (continuation: CheckedContinuation<LevelPlayAdShowResult, Error>) in
168
+ holder.onDisplayFailed = { error in onShowDisplayFailed(holder, continuation, error) }
169
+ holder.onClosed = {
170
+ RetainBox.release(holder)
171
+ continuation.resume(returning: LevelPlayAdShowResult(state: .completed))
172
+ }
173
+ DispatchQueue.main.async {
174
+ holder.ad.showAd(viewController: viewController, placementName: nil)
175
+ }
176
+ }
177
+ case .rewarded:
178
+ let holder = self.withStateLock { self.loadedRewarded.removeValue(forKey: adUnitId) }
179
+ guard let holder else {
180
+ throw levelPlayAdsError("LevelPlay rewarded ad is not ready")
181
+ }
182
+ RetainBox.retain(holder)
183
+ // Rewarded ads don't carry a Unity-style "finish state" — LevelPlay fires a
184
+ // separate didReward event before didCloseAd, so completion is derived from
185
+ // whether that event happened at all before the ad closed.
186
+ var didReceiveReward = false
187
+ return try await withCheckedThrowingContinuation {
188
+ (continuation: CheckedContinuation<LevelPlayAdShowResult, Error>) in
189
+ holder.onReward = { didReceiveReward = true }
190
+ holder.onDisplayFailed = { error in onShowDisplayFailed(holder, continuation, error) }
191
+ holder.onClosed = {
192
+ RetainBox.release(holder)
193
+ let state: LevelPlayAdShowState = didReceiveReward ? .completed : .skipped
194
+ continuation.resume(returning: LevelPlayAdShowResult(state: state))
195
+ }
196
+ DispatchQueue.main.async {
197
+ holder.ad.showAd(viewController: viewController, placementName: nil)
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ func setGDPRConsent(optIn: Bool) throws {
205
+ LPMPrivacySettings.setGDPRConsent(optIn)
206
+ }
207
+
208
+ func setCCPAConsent(optIn: Bool) throws {
209
+ // CCPA flag semantics are "opted out of sale", the inverse of our `optIn`
210
+ // (personalized-ads-allowed) flag.
211
+ LPMPrivacySettings.setCCPA(!optIn)
212
+ }
213
+
214
+ func setCOPPA(isCoppa: Bool) throws {
215
+ LPMPrivacySettings.setCOPPA(isCoppa)
216
+ }
217
+ }
218
+
219
+ private func onShowDisplayFailed<T>(
220
+ _ holder: AnyObject,
221
+ _ continuation: CheckedContinuation<T, Error>,
222
+ _ error: Error
223
+ ) {
224
+ RetainBox.release(holder)
225
+ continuation.resume(throwing: error)
226
+ }
227
+
228
+ // LevelPlay requires the same delegate instance to stay registered from
229
+ // loadAd() through showAd() — swapping in a different delegate right before
230
+ // showAd() (as the old per-phase Load/Show delegate pairs did) is what dropped
231
+ // didCloseAd on Android's equivalent path once the listener was swapped.
232
+ private class AdHolder: NSObject, @unchecked Sendable {
233
+ var onLoaded: (() -> Void)?
234
+ var onLoadFailed: ((Error) -> Void)?
235
+ var onDisplayed: (() -> Void)?
236
+ var onDisplayFailed: ((Error) -> Void)?
237
+ var onClosed: (() -> Void)?
238
+ }
239
+
240
+ private final class InterstitialAdHolder: AdHolder, LPMInterstitialAdDelegate {
241
+ let ad: LPMInterstitialAd
242
+
243
+ init(adUnitId: String) {
244
+ self.ad = LPMInterstitialAd(adUnitId: adUnitId)
245
+ super.init()
246
+ self.ad.setDelegate(self)
247
+ }
248
+
249
+ func didLoadAd(withAdInfo adInfo: LPMAdInfo) { onLoaded?() }
250
+ func didFailToLoadAd(withAdUnitId adUnitId: String, error: Error) { onLoadFailed?(error) }
251
+ func didDisplayAd(withAdInfo adInfo: LPMAdInfo) { onDisplayed?() }
252
+ func didFailToDisplayAd(withAdInfo adInfo: LPMAdInfo, error: Error) { onDisplayFailed?(error) }
253
+ func didClickAd(withAdInfo adInfo: LPMAdInfo) {}
254
+ func didCloseAd(withAdInfo adInfo: LPMAdInfo) { onClosed?() }
255
+ }
256
+
257
+ private final class RewardedAdHolder: AdHolder, LPMRewardedAdDelegate {
258
+ let ad: LPMRewardedAd
259
+ var onReward: (() -> Void)?
260
+
261
+ init(adUnitId: String) {
262
+ self.ad = LPMRewardedAd(adUnitId: adUnitId)
263
+ super.init()
264
+ self.ad.setDelegate(self)
265
+ }
266
+
267
+ func didLoadAd(withAdInfo adInfo: LPMAdInfo) { onLoaded?() }
268
+ func didFailToLoadAd(withAdUnitId adUnitId: String, error: Error) { onLoadFailed?(error) }
269
+ func didDisplayAd(withAdInfo adInfo: LPMAdInfo) { onDisplayed?() }
270
+ func didReward(withAdInfo adInfo: LPMAdInfo, reward: LPMReward) { onReward?() }
271
+ func didFailToDisplayAd(withAdInfo adInfo: LPMAdInfo, error: Error) { onDisplayFailed?(error) }
272
+ func didClickAd(withAdInfo adInfo: LPMAdInfo) {}
273
+ func didCloseAd(withAdInfo adInfo: LPMAdInfo) { onClosed?() }
274
+ }
@@ -0,0 +1,74 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ let levelPlayAdsErrorDomain = "NitroLevelPlayAds"
5
+
6
+ func levelPlayAdsError(_ message: String, code: Int = 1) -> NSError {
7
+ NSError(
8
+ domain: levelPlayAdsErrorDomain,
9
+ code: code,
10
+ userInfo: [NSLocalizedDescriptionKey: message]
11
+ )
12
+ }
13
+
14
+ func runOnMain(_ body: @escaping () -> Void) {
15
+ if Thread.isMainThread {
16
+ body()
17
+ } else {
18
+ DispatchQueue.main.sync(execute: body)
19
+ }
20
+ }
21
+
22
+ func currentViewController() async throws -> UIViewController {
23
+ try await withCheckedThrowingContinuation { continuation in
24
+ DispatchQueue.main.async {
25
+ let root = UIApplication.shared.connectedScenes
26
+ .compactMap { $0 as? UIWindowScene }
27
+ .flatMap(\.windows)
28
+ .first(where: \.isKeyWindow)?
29
+ .rootViewController
30
+
31
+ guard let root else {
32
+ continuation.resume(
33
+ throwing: levelPlayAdsError("Cannot show LevelPlay ad without a view controller")
34
+ )
35
+ return
36
+ }
37
+ continuation.resume(returning: topViewController(root))
38
+ }
39
+ }
40
+ }
41
+
42
+ private func topViewController(_ viewController: UIViewController) -> UIViewController {
43
+ if let presented = viewController.presentedViewController {
44
+ return topViewController(presented)
45
+ }
46
+ if let navigation = viewController as? UINavigationController,
47
+ let visible = navigation.visibleViewController {
48
+ return topViewController(visible)
49
+ }
50
+ if let tab = viewController as? UITabBarController,
51
+ let selected = tab.selectedViewController {
52
+ return topViewController(selected)
53
+ }
54
+ return viewController
55
+ }
56
+
57
+ /// LevelPlay load/show delegates are plain callback objects with no owner —
58
+ /// this keeps them alive for the duration of a single load/show call.
59
+ enum RetainBox {
60
+ private static let lock = NSLock()
61
+ private static var boxes: [ObjectIdentifier: AnyObject] = [:]
62
+
63
+ static func retain(_ object: AnyObject) {
64
+ lock.lock()
65
+ boxes[ObjectIdentifier(object)] = object
66
+ lock.unlock()
67
+ }
68
+
69
+ static func release(_ object: AnyObject) {
70
+ lock.lock()
71
+ boxes.removeValue(forKey: ObjectIdentifier(object))
72
+ lock.unlock()
73
+ }
74
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ import { NitroModules } from 'react-native-nitro-modules';
4
+ const nativeLevelPlayAds = NitroModules.createHybridObject('LevelPlayAds');
5
+ function requireAdUnitId(methodName, adUnitId) {
6
+ if (typeof adUnitId !== 'string' || adUnitId.length === 0) {
7
+ throw new Error(`LevelPlayAds.${methodName}(*) 'adUnitId' expected a non-empty string value.`);
8
+ }
9
+ }
10
+ const levelPlayAdsInstance = {
11
+ initialize(appKey, options) {
12
+ if (typeof appKey !== 'string' || appKey.length === 0) {
13
+ throw new Error("LevelPlayAds.initialize(*) 'appKey' expected a non-empty string value.");
14
+ }
15
+ return nativeLevelPlayAds.initialize(appKey, options?.testMode ?? false);
16
+ },
17
+ load(adType, adUnitId) {
18
+ requireAdUnitId('load', adUnitId);
19
+ return nativeLevelPlayAds.load(adType, adUnitId);
20
+ },
21
+ show(adType, adUnitId) {
22
+ requireAdUnitId('show', adUnitId);
23
+ return nativeLevelPlayAds.show(adType, adUnitId);
24
+ },
25
+ setGDPRConsent: optIn => nativeLevelPlayAds.setGDPRConsent(optIn),
26
+ setCCPAConsent: optIn => nativeLevelPlayAds.setCCPAConsent(optIn),
27
+ setCOPPA: isCoppa => nativeLevelPlayAds.setCOPPA(isCoppa)
28
+ };
29
+ export const LevelPlayAds = () => levelPlayAdsInstance;
30
+ export default LevelPlayAds;
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NitroModules","nativeLevelPlayAds","createHybridObject","requireAdUnitId","methodName","adUnitId","length","Error","levelPlayAdsInstance","initialize","appKey","options","testMode","load","adType","show","setGDPRConsent","optIn","setCCPAConsent","setCOPPA","isCoppa","LevelPlayAds"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AA2BzD,MAAMC,kBAAkB,GACtBD,YAAY,CAACE,kBAAkB,CAAqB,cAAc,CAAC;AAErE,SAASC,eAAeA,CAACC,UAAkB,EAAEC,QAAgB,EAAQ;EACnE,IAAI,OAAOA,QAAQ,KAAK,QAAQ,IAAIA,QAAQ,CAACC,MAAM,KAAK,CAAC,EAAE;IACzD,MAAM,IAAIC,KAAK,CACb,gBAAgBH,UAAU,mDAC5B,CAAC;EACH;AACF;AAEA,MAAMI,oBAAiD,GAAG;EACxDC,UAAUA,CAACC,MAAM,EAAEC,OAAO,EAAE;IAC1B,IAAI,OAAOD,MAAM,KAAK,QAAQ,IAAIA,MAAM,CAACJ,MAAM,KAAK,CAAC,EAAE;MACrD,MAAM,IAAIC,KAAK,CACb,wEACF,CAAC;IACH;IACA,OAAON,kBAAkB,CAACQ,UAAU,CAACC,MAAM,EAAEC,OAAO,EAAEC,QAAQ,IAAI,KAAK,CAAC;EAC1E,CAAC;EACDC,IAAIA,CAACC,MAAM,EAAET,QAAQ,EAAE;IACrBF,eAAe,CAAC,MAAM,EAAEE,QAAQ,CAAC;IACjC,OAAOJ,kBAAkB,CAACY,IAAI,CAACC,MAAM,EAAET,QAAQ,CAAC;EAClD,CAAC;EACDU,IAAIA,CAACD,MAAM,EAAET,QAAQ,EAAE;IACrBF,eAAe,CAAC,MAAM,EAAEE,QAAQ,CAAC;IACjC,OAAOJ,kBAAkB,CAACc,IAAI,CAACD,MAAM,EAAET,QAAQ,CAAC;EAClD,CAAC;EACDW,cAAc,EAAGC,KAAK,IAAKhB,kBAAkB,CAACe,cAAc,CAACC,KAAK,CAAC;EACnEC,cAAc,EAAGD,KAAK,IAAKhB,kBAAkB,CAACiB,cAAc,CAACD,KAAK,CAAC;EACnEE,QAAQ,EAAGC,OAAO,IAAKnB,kBAAkB,CAACkB,QAAQ,CAACC,OAAO;AAC5D,CAAC;AAED,OAAO,MAAMC,YAAY,GAAGA,CAAA,KAC1Bb,oBAAoB;AAEtB,eAAea,YAAY","ignoreList":[]}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ // NitroModules.createHybridObject() is called twice: once by index.ts eagerly
4
+ // at import time, once again below to obtain a reference for assertions. Both
5
+ // calls must return the same object, hence the memoized instance.
6
+ jest.mock('react-native-nitro-modules', () => {
7
+ let instance;
8
+ return {
9
+ NitroModules: {
10
+ createHybridObject: () => {
11
+ instance ??= {
12
+ initialize: jest.fn(async () => undefined),
13
+ load: jest.fn(async () => undefined),
14
+ show: jest.fn(async () => ({
15
+ state: 'completed'
16
+ })),
17
+ setGDPRConsent: jest.fn(),
18
+ setCCPAConsent: jest.fn(),
19
+ setCOPPA: jest.fn()
20
+ };
21
+ return instance;
22
+ }
23
+ }
24
+ };
25
+ });
26
+ import { NitroModules } from 'react-native-nitro-modules';
27
+ import { LevelPlayAds } from "./index.js";
28
+ const mockNativeLevelPlayAds = NitroModules.createHybridObject('LevelPlayAds');
29
+ beforeEach(() => {
30
+ jest.clearAllMocks();
31
+ });
32
+ describe('LevelPlayAds().initialize', () => {
33
+ it('throws for an empty app key', () => {
34
+ expect(() => LevelPlayAds().initialize('')).toThrow(/expected a non-empty string value/);
35
+ });
36
+ it('defaults testMode to false', () => {
37
+ LevelPlayAds().initialize('app-key-1');
38
+ expect(mockNativeLevelPlayAds.initialize).toHaveBeenCalledWith('app-key-1', false);
39
+ });
40
+ it('forwards testMode when provided', () => {
41
+ LevelPlayAds().initialize('app-key-1', {
42
+ testMode: true
43
+ });
44
+ expect(mockNativeLevelPlayAds.initialize).toHaveBeenCalledWith('app-key-1', true);
45
+ });
46
+ });
47
+ describe('LevelPlayAds().load', () => {
48
+ it('throws for an empty ad unit id', () => {
49
+ expect(() => LevelPlayAds().load('interstitial', '')).toThrow(/expected a non-empty string value/);
50
+ });
51
+ it('delegates to the native module', async () => {
52
+ await LevelPlayAds().load('interstitial', 'ad-unit-1');
53
+ expect(mockNativeLevelPlayAds.load).toHaveBeenCalledWith('interstitial', 'ad-unit-1');
54
+ });
55
+ });
56
+ describe('LevelPlayAds().show', () => {
57
+ it('throws for an empty ad unit id', () => {
58
+ expect(() => LevelPlayAds().show('rewarded', '')).toThrow(/expected a non-empty string value/);
59
+ });
60
+ it('resolves with the native show result', async () => {
61
+ const result = await LevelPlayAds().show('rewarded', 'ad-unit-1');
62
+ expect(mockNativeLevelPlayAds.show).toHaveBeenCalledWith('rewarded', 'ad-unit-1');
63
+ expect(result).toEqual({
64
+ state: 'completed'
65
+ });
66
+ });
67
+ });
68
+ describe('LevelPlayAds() consent setters', () => {
69
+ it('forwards GDPR/CCPA/COPPA flags to the native module', () => {
70
+ LevelPlayAds().setGDPRConsent(true);
71
+ LevelPlayAds().setCCPAConsent(false);
72
+ LevelPlayAds().setCOPPA(true);
73
+ expect(mockNativeLevelPlayAds.setGDPRConsent).toHaveBeenCalledWith(true);
74
+ expect(mockNativeLevelPlayAds.setCCPAConsent).toHaveBeenCalledWith(false);
75
+ expect(mockNativeLevelPlayAds.setCOPPA).toHaveBeenCalledWith(true);
76
+ });
77
+ });
78
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["jest","mock","instance","NitroModules","createHybridObject","initialize","fn","undefined","load","show","state","setGDPRConsent","setCCPAConsent","setCOPPA","LevelPlayAds","mockNativeLevelPlayAds","beforeEach","clearAllMocks","describe","it","expect","toThrow","toHaveBeenCalledWith","testMode","result","toEqual"],"sourceRoot":"../../src","sources":["index.test.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=LevelPlayAds.nitro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["specs/LevelPlayAds.nitro.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,16 @@
1
+ import type { LevelPlayAdShowResult, LevelPlayAdType } from './specs/LevelPlayAds.nitro.js';
2
+ export interface LevelPlayAdsInitializeOptions {
3
+ testMode?: boolean;
4
+ }
5
+ export interface LevelPlayAdsModuleInterface {
6
+ initialize(appKey: string, options?: LevelPlayAdsInitializeOptions): Promise<void>;
7
+ load(adType: LevelPlayAdType, adUnitId: string): Promise<void>;
8
+ show(adType: LevelPlayAdType, adUnitId: string): Promise<LevelPlayAdShowResult>;
9
+ setGDPRConsent(optIn: boolean): void;
10
+ setCCPAConsent(optIn: boolean): void;
11
+ setCOPPA(isCoppa: boolean): void;
12
+ }
13
+ export declare const LevelPlayAds: () => LevelPlayAdsModuleInterface;
14
+ export default LevelPlayAds;
15
+ export type { LevelPlayAdShowResult, LevelPlayAdShowState, LevelPlayAdType, LevelPlayAds as LevelPlayAdsSpec, } from './specs/LevelPlayAds.nitro.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EAEhB,MAAM,+BAA4B,CAAC;AAEpC,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CACR,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,CACF,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAClC,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC;AAmCD,eAAO,MAAM,YAAY,QAAO,2BACV,CAAC;AAEvB,eAAe,YAAY,CAAC;AAE5B,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,YAAY,IAAI,gBAAgB,GACjC,MAAM,+BAA4B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ export type LevelPlayAdShowState = 'completed' | 'skipped';
3
+ export interface LevelPlayAdShowResult {
4
+ state: LevelPlayAdShowState;
5
+ }
6
+ export type LevelPlayAdType = 'interstitial' | 'rewarded';
7
+ export interface LevelPlayAds extends HybridObject<{
8
+ ios: 'swift';
9
+ android: 'kotlin';
10
+ }> {
11
+ initialize(appKey: string, testMode: boolean): Promise<void>;
12
+ load(adType: LevelPlayAdType, adUnitId: string): Promise<void>;
13
+ show(adType: LevelPlayAdType, adUnitId: string): Promise<LevelPlayAdShowResult>;
14
+ setGDPRConsent(optIn: boolean): void;
15
+ setCCPAConsent(optIn: boolean): void;
16
+ setCOPPA(isCoppa: boolean): void;
17
+ }
18
+ //# sourceMappingURL=LevelPlayAds.nitro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LevelPlayAds.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/LevelPlayAds.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,oBAAoB,CAAC;CAC7B;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,UAAU,CAAC;AAE1D,MAAM,WAAW,YAAa,SAAQ,YAAY,CAAC;IACjD,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;IACA,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,CACF,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAElC,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC"}
package/nitro.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://nitro.margelo.com/nitro.schema.json",
3
+ "cxxNamespace": ["levelplayads"],
4
+ "ios": {
5
+ "iosModuleName": "NitroLevelPlayAds"
6
+ },
7
+ "android": {
8
+ "androidNamespace": ["levelplayads"],
9
+ "androidCxxLibName": "NitroLevelPlayAds"
10
+ },
11
+ "autolinking": {
12
+ "LevelPlayAds": {
13
+ "swift": "HybridLevelPlayAds",
14
+ "kotlin": "HybridLevelPlayAds"
15
+ }
16
+ },
17
+ "ignorePaths": ["**/node_modules"]
18
+ }
@@ -0,0 +1,81 @@
1
+ #
2
+ # NitroLevelPlayAds+autolinking.cmake
3
+ # This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ # https://github.com/mrousavy/nitro
5
+ # Copyright © 2026 Marc Rousavy @ Margelo
6
+ #
7
+
8
+ # This is a CMake file that adds all files generated by Nitrogen
9
+ # to the current CMake project.
10
+ #
11
+ # To use it, add this to your CMakeLists.txt:
12
+ # ```cmake
13
+ # include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroLevelPlayAds+autolinking.cmake)
14
+ # ```
15
+
16
+ # Define a flag to check if we are building properly
17
+ add_definitions(-DBUILDING_NITROLEVELPLAYADS_WITH_GENERATED_CMAKE_PROJECT)
18
+
19
+ # Enable Raw Props parsing in react-native (for Nitro Views)
20
+ add_definitions(-DRN_SERIALIZABLE_STATE)
21
+
22
+ # Add all headers that were generated by Nitrogen
23
+ include_directories(
24
+ "../nitrogen/generated/shared/c++"
25
+ "../nitrogen/generated/android/c++"
26
+ "../nitrogen/generated/android/"
27
+ )
28
+
29
+ # Add all .cpp sources that were generated by Nitrogen
30
+ target_sources(
31
+ # CMake project name (Android C++ library name)
32
+ NitroLevelPlayAds PRIVATE
33
+ # Autolinking Setup
34
+ ../nitrogen/generated/android/NitroLevelPlayAdsOnLoad.cpp
35
+ # Shared Nitrogen C++ sources
36
+ ../nitrogen/generated/shared/c++/HybridLevelPlayAdsSpec.cpp
37
+ # Android-specific Nitrogen C++ sources
38
+ ../nitrogen/generated/android/c++/JHybridLevelPlayAdsSpec.cpp
39
+ )
40
+
41
+ # From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake
42
+ # Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake
43
+ target_compile_definitions(
44
+ NitroLevelPlayAds PRIVATE
45
+ -DFOLLY_NO_CONFIG=1
46
+ -DFOLLY_HAVE_CLOCK_GETTIME=1
47
+ -DFOLLY_USE_LIBCPP=1
48
+ -DFOLLY_CFG_NO_COROUTINES=1
49
+ -DFOLLY_MOBILE=1
50
+ -DFOLLY_HAVE_RECVMMSG=1
51
+ -DFOLLY_HAVE_PTHREAD=1
52
+ # Once we target android-23 above, we can comment
53
+ # the following line. NDK uses GNU style stderror_r() after API 23.
54
+ -DFOLLY_HAVE_XSI_STRERROR_R=1
55
+ )
56
+
57
+ # Add all libraries required by the generated specs
58
+ find_package(fbjni REQUIRED) # <-- Used for communication between Java <-> C++
59
+ find_package(ReactAndroid REQUIRED) # <-- Used to set up React Native bindings (e.g. CallInvoker/TurboModule)
60
+ find_package(react-native-nitro-modules REQUIRED) # <-- Used to create all HybridObjects and use the Nitro core library
61
+
62
+ # Link all libraries together
63
+ target_link_libraries(
64
+ NitroLevelPlayAds
65
+ fbjni::fbjni # <-- Facebook C++ JNI helpers
66
+ ReactAndroid::jsi # <-- RN: JSI
67
+ react-native-nitro-modules::NitroModules # <-- NitroModules Core :)
68
+ )
69
+
70
+ # Link react-native (different prefab between RN 0.75 and RN 0.76)
71
+ if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
72
+ target_link_libraries(
73
+ NitroLevelPlayAds
74
+ ReactAndroid::reactnative # <-- RN: Native Modules umbrella prefab
75
+ )
76
+ else()
77
+ target_link_libraries(
78
+ NitroLevelPlayAds
79
+ ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core
80
+ )
81
+ endif()
@@ -0,0 +1,27 @@
1
+ ///
2
+ /// NitroLevelPlayAds+autolinking.gradle
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ /// This is a Gradle file that adds all files generated by Nitrogen
9
+ /// to the current Gradle project.
10
+ ///
11
+ /// To use it, add this to your build.gradle:
12
+ /// ```gradle
13
+ /// apply from: '../nitrogen/generated/android/NitroLevelPlayAds+autolinking.gradle'
14
+ /// ```
15
+
16
+ logger.warn("[NitroModules] 🔥 NitroLevelPlayAds is boosted by nitro!")
17
+
18
+ android {
19
+ sourceSets {
20
+ main {
21
+ java.srcDirs += [
22
+ // Nitrogen files
23
+ "${project.projectDir}/../nitrogen/generated/android/kotlin"
24
+ ]
25
+ }
26
+ }
27
+ }