@ezoic/react-native-sdk 1.0.0 → 1.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.
@@ -20,6 +20,6 @@ Pod::Spec.new do |s|
20
20
 
21
21
  # Native Ezoic Ads SDK (vends the `EzoicAdsSDKBinary` module). Brings in
22
22
  # PrebidMobile + Google-Mobile-Ads-SDK transitively.
23
- s.dependency "EzoicAdsSDK", "~> 1.0"
23
+ s.dependency "EzoicAdsSDK", "~> 1.1"
24
24
  s.swift_version = "5.9"
25
25
  end
@@ -68,5 +68,5 @@ dependencies {
68
68
  // Native Ezoic Ads SDK (resolved from Maven Central). Brings in Google Mobile
69
69
  // Ads + Prebid transitively. Requires mavenCentral() + google() in the
70
70
  // consuming app's repositories (the RN template provides both).
71
- implementation "com.ezoic.sdk:ezoic-ads-sdk:1.0.0"
71
+ implementation "com.ezoic.sdk:ezoic-ads-sdk:1.1.0"
72
72
  }
@@ -1,17 +1,37 @@
1
1
  package com.ezoic.reactnative
2
2
 
3
3
  import android.app.Application
4
+ import com.facebook.react.bridge.Arguments
4
5
  import com.facebook.react.bridge.Promise
5
6
  import com.facebook.react.bridge.ReactApplicationContext
6
7
  import com.facebook.react.bridge.ReadableMap
8
+ import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.modules.core.DeviceEventManagerModule
10
+ import com.ezoic.ads.sdk.adunits.EzoicReward
11
+ import com.ezoic.ads.sdk.adunits.EzoicRewardedAd
12
+ import com.ezoic.ads.sdk.adunits.EzoicRewardedAdListener
13
+ import com.ezoic.ads.sdk.adunits.EzoicRewardedAdListenerAdapter
7
14
  import com.ezoic.ads.sdk.core.EzoicAds
8
15
  import com.ezoic.ads.sdk.core.EzoicConfiguration
16
+ import com.ezoic.ads.sdk.core.EzoicError
17
+ import java.util.concurrent.ConcurrentHashMap
9
18
 
10
19
  class EzoicAdsModule(reactContext: ReactApplicationContext) :
11
20
  NativeEzoicAdsSpec(reactContext) {
12
21
 
13
22
  override fun getName() = NAME
14
23
 
24
+ /** Loaded rewarded ads awaiting `show`, keyed by ad unit id. */
25
+ private val rewardedAds = ConcurrentHashMap<Int, EzoicRewardedAd>()
26
+
27
+ /** In-flight `show` calls, keyed by ad unit id. */
28
+ private val pendingShows = ConcurrentHashMap<Int, RewardShow>()
29
+
30
+ private class RewardShow(val promise: Promise) {
31
+ @Volatile var settled = false
32
+ @Volatile var reward: EzoicReward? = null
33
+ }
34
+
15
35
  override fun initialize(config: ReadableMap, promise: Promise) {
16
36
  val domain = if (config.hasKey("domain")) config.getString("domain") else null
17
37
  if (domain.isNullOrEmpty()) {
@@ -53,10 +73,135 @@ class EzoicAdsModule(reactContext: ReactApplicationContext) :
53
73
  EzoicAds.instance.trackPageview { success -> promise.resolve(success) }
54
74
  }
55
75
 
76
+ override fun loadRewardedAd(adUnitIdentifier: String, promise: Promise) {
77
+ val id = adUnitIdentifier.toIntOrNull()
78
+ if (id == null) {
79
+ promise.reject("EzoicAds", "Invalid adUnitIdentifier: $adUnitIdentifier")
80
+ return
81
+ }
82
+ EzoicRewardedAd.load(reactApplicationContext, id) { result ->
83
+ result.onSuccess { ad ->
84
+ ad.listener = makeListener(adUnitIdentifier)
85
+ rewardedAds[id] = ad
86
+ promise.resolve(null)
87
+ }.onFailure { e ->
88
+ promise.reject("EzoicAds", e.message ?: "Rewarded ad failed to load", e)
89
+ }
90
+ }
91
+ }
92
+
93
+ override fun showRewardedAd(adUnitIdentifier: String, promise: Promise) {
94
+ val id = adUnitIdentifier.toIntOrNull()
95
+ val ad = if (id != null) rewardedAds[id] else null
96
+ if (id == null || ad == null) {
97
+ promise.reject("EzoicAds", "Rewarded ad not loaded for $adUnitIdentifier")
98
+ return
99
+ }
100
+ val activity = currentActivity
101
+ if (activity == null) {
102
+ promise.reject("EzoicAds", "No current Activity to present the rewarded ad")
103
+ return
104
+ }
105
+
106
+ val show = RewardShow(promise)
107
+ pendingShows[id] = show
108
+
109
+ // Replace the load-time listener with one that also settles the promise on
110
+ // terminal events (dismiss = resolve, failed-to-show = reject).
111
+ ad.listener = makeListener(
112
+ adUnitIdentifier,
113
+ onDismiss = {
114
+ rewardedAds.remove(id)
115
+ val pending = pendingShows.remove(id)
116
+ if (pending != null && !pending.settled) {
117
+ pending.settled = true
118
+ val reward = pending.reward
119
+ val map = Arguments.createMap()
120
+ map.putBoolean("earned", reward != null)
121
+ map.putString("type", reward?.type ?: "")
122
+ map.putDouble("amount", (reward?.amount ?: 0).toDouble())
123
+ pending.promise.resolve(map)
124
+ }
125
+ },
126
+ onFailedToShow = { message ->
127
+ rewardedAds.remove(id)
128
+ val pending = pendingShows.remove(id)
129
+ if (pending != null && !pending.settled) {
130
+ pending.settled = true
131
+ pending.promise.reject("EzoicAds", message)
132
+ }
133
+ }
134
+ )
135
+
136
+ activity.runOnUiThread {
137
+ ad.show(activity) { reward -> show.reward = reward }
138
+ }
139
+ }
140
+
141
+ override fun addListener(eventName: String) {
142
+ // No-op: required by the React Native NativeEventEmitter contract.
143
+ }
144
+
145
+ override fun removeListeners(count: Double) {
146
+ // No-op: required by the React Native NativeEventEmitter contract.
147
+ }
148
+
149
+ private fun makeListener(
150
+ adUnitIdentifier: String,
151
+ onDismiss: (() -> Unit)? = null,
152
+ onFailedToShow: ((String) -> Unit)? = null
153
+ ): EzoicRewardedAdListener = object : EzoicRewardedAdListenerAdapter() {
154
+ override fun onRewardedAdShown(rewardedAd: EzoicRewardedAd) {
155
+ emitRewardedEvent(adUnitIdentifier, "shown")
156
+ }
157
+
158
+ override fun onRewardedAdFailedToShow(rewardedAd: EzoicRewardedAd, error: EzoicError) {
159
+ emitRewardedEvent(adUnitIdentifier, "failedToShow") {
160
+ putString("message", error.message)
161
+ }
162
+ onFailedToShow?.invoke(error.message)
163
+ }
164
+
165
+ override fun onRewardedAdImpression(rewardedAd: EzoicRewardedAd) {
166
+ emitRewardedEvent(adUnitIdentifier, "impression")
167
+ }
168
+
169
+ override fun onRewardedAdClicked(rewardedAd: EzoicRewardedAd) {
170
+ emitRewardedEvent(adUnitIdentifier, "clicked")
171
+ }
172
+
173
+ override fun onUserEarnedReward(rewardedAd: EzoicRewardedAd, reward: EzoicReward) {
174
+ emitRewardedEvent(adUnitIdentifier, "reward") {
175
+ putString("rewardType", reward.type)
176
+ putDouble("rewardAmount", reward.amount.toDouble())
177
+ }
178
+ }
179
+
180
+ override fun onRewardedAdDismissed(rewardedAd: EzoicRewardedAd) {
181
+ emitRewardedEvent(adUnitIdentifier, "dismissed")
182
+ onDismiss?.invoke()
183
+ }
184
+ }
185
+
186
+ private fun emitRewardedEvent(
187
+ adUnitIdentifier: String,
188
+ type: String,
189
+ extra: (WritableMap.() -> Unit)? = null
190
+ ) {
191
+ val map = Arguments.createMap()
192
+ map.putString("adUnitIdentifier", adUnitIdentifier)
193
+ map.putString("type", type)
194
+ extra?.invoke(map)
195
+ reactApplicationContext
196
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
197
+ .emit(REWARDED_EVENT, map)
198
+ }
199
+
56
200
  private fun ReadableMap.optBool(key: String, default: Boolean): Boolean =
57
201
  if (hasKey(key) && !isNull(key)) getBoolean(key) else default
58
202
 
59
203
  companion object {
60
204
  const val NAME = NativeEzoicAdsSpec.NAME
205
+ private const val REWARDED_EVENT = "EzoicRewardedAdEvent"
61
206
  }
62
207
  }
@@ -3,6 +3,25 @@ import EzoicAdsSDKBinary
3
3
 
4
4
  @objc public class EzoicAdsImpl: NSObject {
5
5
 
6
+ /// Set by the Obj-C module to forward rewarded lifecycle events to JS.
7
+ @objc public var eventEmitter: ((String, [String: Any]) -> Void)?
8
+
9
+ /// Loaded rewarded ads awaiting `show`, keyed by ad unit id.
10
+ private var rewardedAds: [Int: EzoicRewardedAd] = [:]
11
+
12
+ /// In-flight `show` calls, keyed by ad unit id.
13
+ private var pendingShows: [Int: PendingRewardShow] = [:]
14
+
15
+ private final class PendingRewardShow {
16
+ let resolve: (Any?) -> Void
17
+ let reject: (String, String, NSError?) -> Void
18
+ var reward: EzoicReward?
19
+ init(resolve: @escaping (Any?) -> Void, reject: @escaping (String, String, NSError?) -> Void) {
20
+ self.resolve = resolve
21
+ self.reject = reject
22
+ }
23
+ }
24
+
6
25
  @objc public func initialize(_ config: NSDictionary,
7
26
  resolve: @escaping (Any?) -> Void,
8
27
  reject: @escaping (String, String, NSError?) -> Void) {
@@ -45,4 +64,93 @@ import EzoicAdsSDKBinary
45
64
  resolve(NSNumber(value: success))
46
65
  }
47
66
  }
67
+
68
+ @objc public func loadRewardedAd(_ adUnitIdentifier: String,
69
+ resolve: @escaping (Any?) -> Void,
70
+ reject: @escaping (String, String, NSError?) -> Void) {
71
+ guard let id = Int(adUnitIdentifier) else {
72
+ reject("EzoicAds", "Invalid adUnitIdentifier: \(adUnitIdentifier)", nil)
73
+ return
74
+ }
75
+ EzoicRewardedAd.load(adUnitIdentifier: id) { [weak self] result in
76
+ guard let self = self else { return }
77
+ switch result {
78
+ case .success(let ad):
79
+ ad.delegate = self
80
+ self.rewardedAds[id] = ad
81
+ resolve(nil)
82
+ case .failure(let error):
83
+ reject("EzoicAds", error.localizedDescription, error as NSError)
84
+ }
85
+ }
86
+ }
87
+
88
+ @objc public func showRewardedAd(_ adUnitIdentifier: String,
89
+ resolve: @escaping (Any?) -> Void,
90
+ reject: @escaping (String, String, NSError?) -> Void) {
91
+ guard let id = Int(adUnitIdentifier), let ad = rewardedAds[id] else {
92
+ reject("EzoicAds", "Rewarded ad not loaded for \(adUnitIdentifier)", nil)
93
+ return
94
+ }
95
+ pendingShows[id] = PendingRewardShow(resolve: resolve, reject: reject)
96
+ // Presenting from nil lets GMA use the application's top view controller.
97
+ ad.show(from: nil) { [weak self] reward in
98
+ self?.pendingShows[id]?.reward = reward
99
+ }
100
+ }
101
+
102
+ private func emit(_ ad: EzoicRewardedAd, _ type: String, _ extra: [String: Any] = [:]) {
103
+ var body: [String: Any] = [
104
+ "adUnitIdentifier": String(ad.adUnitIdentifier),
105
+ "type": type
106
+ ]
107
+ for (key, value) in extra { body[key] = value }
108
+ eventEmitter?("EzoicRewardedAdEvent", body)
109
+ }
110
+ }
111
+
112
+ // MARK: - EzoicRewardedAdDelegate
113
+
114
+ extension EzoicAdsImpl: EzoicRewardedAdDelegate {
115
+
116
+ public func rewardedAdDidPresent(_ rewardedAd: EzoicRewardedAd) {
117
+ emit(rewardedAd, "shown")
118
+ }
119
+
120
+ public func rewardedAd(_ rewardedAd: EzoicRewardedAd, didFailToPresentWithError error: EzoicError) {
121
+ emit(rewardedAd, "failedToShow", ["message": error.localizedDescription])
122
+ let id = rewardedAd.adUnitIdentifier
123
+ rewardedAds.removeValue(forKey: id)
124
+ if let pending = pendingShows.removeValue(forKey: id) {
125
+ pending.reject("EzoicAds", error.localizedDescription, error as NSError)
126
+ }
127
+ }
128
+
129
+ public func rewardedAdDidRecordImpression(_ rewardedAd: EzoicRewardedAd) {
130
+ emit(rewardedAd, "impression")
131
+ }
132
+
133
+ public func rewardedAdDidRecordClick(_ rewardedAd: EzoicRewardedAd) {
134
+ emit(rewardedAd, "clicked")
135
+ }
136
+
137
+ public func rewardedAd(_ rewardedAd: EzoicRewardedAd, userDidEarn reward: EzoicReward) {
138
+ emit(rewardedAd, "reward", ["rewardType": reward.type, "rewardAmount": reward.amount])
139
+ pendingShows[rewardedAd.adUnitIdentifier]?.reward = reward
140
+ }
141
+
142
+ public func rewardedAdDidDismiss(_ rewardedAd: EzoicRewardedAd) {
143
+ emit(rewardedAd, "dismissed")
144
+ let id = rewardedAd.adUnitIdentifier
145
+ rewardedAds.removeValue(forKey: id)
146
+ if let pending = pendingShows.removeValue(forKey: id) {
147
+ let reward = pending.reward
148
+ let result: [String: Any] = [
149
+ "earned": reward != nil,
150
+ "type": reward?.type ?? "",
151
+ "amount": reward?.amount ?? 0
152
+ ]
153
+ pending.resolve(result)
154
+ }
155
+ }
48
156
  }
@@ -1,5 +1,8 @@
1
1
  #import <EzoicReactNativeSdkSpec/EzoicReactNativeSdkSpec.h>
2
+ #import <React/RCTEventEmitter.h>
2
3
 
3
- @interface EzoicReactNativeSdk : NSObject <NativeEzoicAdsSpec>
4
+ // Subclasses RCTEventEmitter so the rewarded ad lifecycle can be surfaced to
5
+ // JS via NativeEventEmitter, while still vending the codegen'd TurboModule.
6
+ @interface EzoicReactNativeSdk : RCTEventEmitter <NativeEzoicAdsSpec>
4
7
 
5
8
  @end
@@ -1,17 +1,46 @@
1
1
  #import "EzoicReactNativeSdk.h"
2
2
  #import <EzoicReactNativeSdk/EzoicReactNativeSdk-Swift.h>
3
3
 
4
+ static NSString *const kEzoicRewardedEvent = @"EzoicRewardedAdEvent";
5
+
4
6
  @implementation EzoicReactNativeSdk {
5
7
  EzoicAdsImpl *_impl;
8
+ BOOL _hasListeners;
6
9
  }
7
10
 
8
11
  - (instancetype)init {
9
12
  if (self = [super init]) {
10
13
  _impl = [EzoicAdsImpl new];
14
+ __weak __typeof(self) weakSelf = self;
15
+ // The Swift impl invokes this for every rewarded lifecycle signal; forward
16
+ // to JS through RCTEventEmitter (guarded by an active-listener check so we
17
+ // don't log the "no listeners" warning when nothing is subscribed).
18
+ _impl.eventEmitter = ^(NSString *name, NSDictionary *body) {
19
+ __typeof(self) strongSelf = weakSelf;
20
+ if (strongSelf != nil && strongSelf->_hasListeners) {
21
+ [strongSelf sendEventWithName:name body:body];
22
+ }
23
+ };
11
24
  }
12
25
  return self;
13
26
  }
14
27
 
28
+ + (BOOL)requiresMainQueueSetup {
29
+ return NO;
30
+ }
31
+
32
+ - (NSArray<NSString *> *)supportedEvents {
33
+ return @[ kEzoicRewardedEvent ];
34
+ }
35
+
36
+ - (void)startObserving {
37
+ _hasListeners = YES;
38
+ }
39
+
40
+ - (void)stopObserving {
41
+ _hasListeners = NO;
42
+ }
43
+
15
44
  - (void)initialize:(JS::NativeEzoicAds::EzoicConfig &)config
16
45
  resolve:(RCTPromiseResolveBlock)resolve
17
46
  reject:(RCTPromiseRejectBlock)reject {
@@ -43,6 +72,22 @@
43
72
  [_impl trackPageview:^(id _Nullable v) { resolve(v); }];
44
73
  }
45
74
 
75
+ - (void)loadRewardedAd:(NSString *)adUnitIdentifier
76
+ resolve:(RCTPromiseResolveBlock)resolve
77
+ reject:(RCTPromiseRejectBlock)reject {
78
+ [_impl loadRewardedAd:adUnitIdentifier
79
+ resolve:^(id _Nullable v) { resolve(v); }
80
+ reject:^(NSString *code, NSString *msg, NSError *_Nullable e) { reject(code, msg, e); }];
81
+ }
82
+
83
+ - (void)showRewardedAd:(NSString *)adUnitIdentifier
84
+ resolve:(RCTPromiseResolveBlock)resolve
85
+ reject:(RCTPromiseRejectBlock)reject {
86
+ [_impl showRewardedAd:adUnitIdentifier
87
+ resolve:^(id _Nullable v) { resolve(v); }
88
+ reject:^(NSString *code, NSString *msg, NSError *_Nullable e) { reject(code, msg, e); }];
89
+ }
90
+
46
91
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
47
92
  (const facebook::react::ObjCTurboModule::InitParams &)params {
48
93
  return std::make_shared<facebook::react::NativeEzoicAdsSpecJSI>(params);
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ import { NativeEventEmitter } from 'react-native';
4
+ import NativeEzoicAds from "./NativeEzoicAds.js";
5
+ import { coerceAdUnitId, mapRewardResult } from "./helpers.js";
6
+
7
+ /** A reward earned by the user for completing a rewarded ad. */
8
+
9
+ /** Lifecycle callbacks for a rewarded ad. All are optional. */
10
+
11
+ /** The single native event name carrying every rewarded lifecycle signal. */
12
+ const REWARDED_EVENT = 'EzoicRewardedAdEvent';
13
+ // A single shared emitter is sufficient — events are routed to the right
14
+ // instance by adUnitIdentifier below.
15
+ const emitter = new NativeEventEmitter(NativeEzoicAds);
16
+
17
+ /**
18
+ * A rewarded ad. Use the static `load` to fetch an ad ahead of time, then call
19
+ * `show()` to present it and grant the reward when the user finishes watching.
20
+ *
21
+ * ```ts
22
+ * const ad = await EzoicRewardedAd.load('12345');
23
+ * ad.setListeners({ onDismissed: () => console.log('closed') });
24
+ * const reward = await ad.show();
25
+ * if (reward) grantReward(reward.amount);
26
+ * ```
27
+ *
28
+ * Mirrors the native `EzoicRewardedAd` load/show lifecycle on both platforms.
29
+ * Rewarded ads are single-use — load a new one for the next opportunity.
30
+ */
31
+ export class EzoicRewardedAd {
32
+ /** The Ezoic ad unit identifier this ad was loaded for. */
33
+
34
+ listeners = {};
35
+ subscription = null;
36
+ constructor(adUnitIdentifier) {
37
+ this.adUnitIdentifier = adUnitIdentifier;
38
+ this.subscription = emitter.addListener(REWARDED_EVENT, raw => {
39
+ const event = raw;
40
+ if (event.adUnitIdentifier !== this.adUnitIdentifier) return;
41
+ this.handleEvent(event);
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Loads a rewarded ad for the given Ezoic ad unit identifier. Resolves with
47
+ * a ready-to-show `EzoicRewardedAd`, or rejects if no ad could be loaded.
48
+ */
49
+ static async load(adUnitIdentifier) {
50
+ const id = coerceAdUnitId(adUnitIdentifier);
51
+ const ad = new EzoicRewardedAd(id);
52
+ try {
53
+ await NativeEzoicAds.loadRewardedAd(id);
54
+ return ad;
55
+ } catch (error) {
56
+ ad.destroy();
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ /** Registers lifecycle callbacks. Replaces any previously set listeners. */
62
+ setListeners(listeners) {
63
+ this.listeners = listeners;
64
+ }
65
+
66
+ /**
67
+ * Presents the rewarded ad full-screen. Resolves with the earned reward, or
68
+ * `null` if the ad was dismissed before the reward was earned. Rejects if the
69
+ * ad was not ready (load first) or failed to present.
70
+ */
71
+ async show() {
72
+ const result = await NativeEzoicAds.showRewardedAd(this.adUnitIdentifier);
73
+ return mapRewardResult(result);
74
+ }
75
+
76
+ /** Releases the event subscription. Safe to call multiple times. */
77
+ destroy() {
78
+ this.subscription?.remove();
79
+ this.subscription = null;
80
+ this.listeners = {};
81
+ }
82
+ handleEvent(event) {
83
+ switch (event.type) {
84
+ case 'shown':
85
+ this.listeners.onShown?.();
86
+ break;
87
+ case 'failedToShow':
88
+ this.listeners.onFailedToShow?.({
89
+ message: event.message ?? ''
90
+ });
91
+ break;
92
+ case 'impression':
93
+ this.listeners.onImpression?.();
94
+ break;
95
+ case 'clicked':
96
+ this.listeners.onClicked?.();
97
+ break;
98
+ case 'reward':
99
+ this.listeners.onUserEarnedReward?.({
100
+ type: event.rewardType ?? '',
101
+ amount: event.rewardAmount ?? 0
102
+ });
103
+ break;
104
+ case 'dismissed':
105
+ this.listeners.onDismissed?.();
106
+ // Dismissal is terminal — the native ad is single-use.
107
+ this.destroy();
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ //# sourceMappingURL=EzoicRewardedAd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeEventEmitter","NativeEzoicAds","coerceAdUnitId","mapRewardResult","REWARDED_EVENT","emitter","EzoicRewardedAd","listeners","subscription","constructor","adUnitIdentifier","addListener","raw","event","handleEvent","load","id","ad","loadRewardedAd","error","destroy","setListeners","show","result","showRewardedAd","remove","type","onShown","onFailedToShow","message","onImpression","onClicked","onUserEarnedReward","rewardType","amount","rewardAmount","onDismissed"],"sourceRoot":"../../src","sources":["EzoicRewardedAd.ts"],"mappings":";;AAAA,SAASA,kBAAkB,QAAkC,cAAc;AAC3E,OAAOC,cAAc,MAAM,qBAAkB;AAC7C,SAASC,cAAc,EAAEC,eAAe,QAAQ,cAAW;;AAE3D;;AAMA;;AAUA;AACA,MAAMC,cAAc,GAAG,sBAAsB;AAgB7C;AACA;AACA,MAAMC,OAAO,GAAG,IAAIL,kBAAkB,CAACC,cAAuB,CAAC;;AAE/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMK,eAAe,CAAC;EAC3B;;EAGQC,SAAS,GAA6B,CAAC,CAAC;EACxCC,YAAY,GAA+B,IAAI;EAE/CC,WAAWA,CAACC,gBAAwB,EAAE;IAC5C,IAAI,CAACA,gBAAgB,GAAGA,gBAAgB;IACxC,IAAI,CAACF,YAAY,GAAGH,OAAO,CAACM,WAAW,CAACP,cAAc,EAAGQ,GAAY,IAAK;MACxE,MAAMC,KAAK,GAAGD,GAA0B;MACxC,IAAIC,KAAK,CAACH,gBAAgB,KAAK,IAAI,CAACA,gBAAgB,EAAE;MACtD,IAAI,CAACI,WAAW,CAACD,KAAK,CAAC;IACzB,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;EACE,aAAaE,IAAIA,CAACL,gBAAwB,EAA4B;IACpE,MAAMM,EAAE,GAAGd,cAAc,CAACQ,gBAAgB,CAAC;IAC3C,MAAMO,EAAE,GAAG,IAAIX,eAAe,CAACU,EAAE,CAAC;IAClC,IAAI;MACF,MAAMf,cAAc,CAACiB,cAAc,CAACF,EAAE,CAAC;MACvC,OAAOC,EAAE;IACX,CAAC,CAAC,OAAOE,KAAK,EAAE;MACdF,EAAE,CAACG,OAAO,CAAC,CAAC;MACZ,MAAMD,KAAK;IACb;EACF;;EAEA;EACAE,YAAYA,CAACd,SAAmC,EAAQ;IACtD,IAAI,CAACA,SAAS,GAAGA,SAAS;EAC5B;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMe,IAAIA,CAAA,EAAgC;IACxC,MAAMC,MAAM,GAAG,MAAMtB,cAAc,CAACuB,cAAc,CAAC,IAAI,CAACd,gBAAgB,CAAC;IACzE,OAAOP,eAAe,CAACoB,MAAM,CAAC;EAChC;;EAEA;EACAH,OAAOA,CAAA,EAAS;IACd,IAAI,CAACZ,YAAY,EAAEiB,MAAM,CAAC,CAAC;IAC3B,IAAI,CAACjB,YAAY,GAAG,IAAI;IACxB,IAAI,CAACD,SAAS,GAAG,CAAC,CAAC;EACrB;EAEQO,WAAWA,CAACD,KAA0B,EAAQ;IACpD,QAAQA,KAAK,CAACa,IAAI;MAChB,KAAK,OAAO;QACV,IAAI,CAACnB,SAAS,CAACoB,OAAO,GAAG,CAAC;QAC1B;MACF,KAAK,cAAc;QACjB,IAAI,CAACpB,SAAS,CAACqB,cAAc,GAAG;UAAEC,OAAO,EAAEhB,KAAK,CAACgB,OAAO,IAAI;QAAG,CAAC,CAAC;QACjE;MACF,KAAK,YAAY;QACf,IAAI,CAACtB,SAAS,CAACuB,YAAY,GAAG,CAAC;QAC/B;MACF,KAAK,SAAS;QACZ,IAAI,CAACvB,SAAS,CAACwB,SAAS,GAAG,CAAC;QAC5B;MACF,KAAK,QAAQ;QACX,IAAI,CAACxB,SAAS,CAACyB,kBAAkB,GAAG;UAClCN,IAAI,EAAEb,KAAK,CAACoB,UAAU,IAAI,EAAE;UAC5BC,MAAM,EAAErB,KAAK,CAACsB,YAAY,IAAI;QAChC,CAAC,CAAC;QACF;MACF,KAAK,WAAW;QACd,IAAI,CAAC5B,SAAS,CAAC6B,WAAW,GAAG,CAAC;QAC9B;QACA,IAAI,CAAChB,OAAO,CAAC,CAAC;QACd;IACJ;EACF;AACF","ignoreList":[]}
@@ -1,5 +1,13 @@
1
1
  "use strict";
2
2
 
3
3
  import { TurboModuleRegistry } from 'react-native';
4
+
5
+ /**
6
+ * Result of `showRewardedAd`. `earned` is true when the user completed the ad
7
+ * and earned the reward; `type`/`amount` describe the granted reward (empty/0
8
+ * when the ad was dismissed without earning). The public `EzoicRewardedAd.show`
9
+ * maps this to `EzoicReward | null`.
10
+ */
11
+
4
12
  export default TurboModuleRegistry.getEnforcing('EzoicReactNativeSdk');
5
13
  //# sourceMappingURL=NativeEzoicAds.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeEzoicAds.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAmBlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,qBAAqB,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeEzoicAds.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAWlD;AACA;AACA;AACA;AACA;AACA;;AAoBA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,qBAAqB,CAAC","ignoreList":[]}
@@ -21,4 +21,17 @@ export function normalizeSize(size) {
21
21
  export function coerceAdUnitId(adUnitIdentifier) {
22
22
  return String(adUnitIdentifier);
23
23
  }
24
+ /**
25
+ * Maps the native `showRewardedAd` result to the public reward shape: the
26
+ * `{ type, amount }` reward when earned, otherwise `null` (dismissed unearned).
27
+ */
28
+ export function mapRewardResult(result) {
29
+ if (result && result.earned) {
30
+ return {
31
+ type: result.type,
32
+ amount: result.amount
33
+ };
34
+ }
35
+ return null;
36
+ }
24
37
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["normalizeConfig","config","domain","Error","out","autoReadConsent","undefined","subjectToCOPPA","requestATTBeforeAds","debugEnabled","testMode","normalizeSize","size","split","map","s","trim","filter","length","join","coerceAdUnitId","adUnitIdentifier","String"],"sourceRoot":"../../src","sources":["helpers.ts"],"mappings":";;AAEA,OAAO,SAASA,eAAeA,CAACC,MAAmB,EAAe;EAChE,IAAI,CAACA,MAAM,IAAI,CAACA,MAAM,CAACC,MAAM,EAAE;IAC7B,MAAM,IAAIC,KAAK,CAAC,oDAAoD,CAAC;EACvE;EACA,MAAMC,GAAgB,GAAG;IAAEF,MAAM,EAAED,MAAM,CAACC;EAAO,CAAC;EAClD,IAAID,MAAM,CAACI,eAAe,KAAKC,SAAS,EACtCF,GAAG,CAACC,eAAe,GAAGJ,MAAM,CAACI,eAAe;EAC9C,IAAIJ,MAAM,CAACM,cAAc,KAAKD,SAAS,EACrCF,GAAG,CAACG,cAAc,GAAGN,MAAM,CAACM,cAAc;EAC5C,IAAIN,MAAM,CAACO,mBAAmB,KAAKF,SAAS,EAC1CF,GAAG,CAACI,mBAAmB,GAAGP,MAAM,CAACO,mBAAmB;EACtD,IAAIP,MAAM,CAACQ,YAAY,KAAKH,SAAS,EAAEF,GAAG,CAACK,YAAY,GAAGR,MAAM,CAACQ,YAAY;EAC7E,IAAIR,MAAM,CAACS,QAAQ,KAAKJ,SAAS,EAAEF,GAAG,CAACM,QAAQ,GAAGT,MAAM,CAACS,QAAQ;EACjE,OAAON,GAAG;AACZ;AAEA,OAAO,SAASO,aAAaA,CAACC,IAAwB,EAAU;EAC9D,IAAI,CAACA,IAAI,EAAE,OAAO,EAAE;EACpB,OAAOA,IAAI,CACRC,KAAK,CAAC,GAAG,CAAC,CACVC,GAAG,CAAEC,CAAC,IAAKA,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC,CACpBC,MAAM,CAAEF,CAAC,IAAKA,CAAC,CAACG,MAAM,GAAG,CAAC,CAAC,CAC3BC,IAAI,CAAC,GAAG,CAAC;AACd;AAEA,OAAO,SAASC,cAAcA,CAACC,gBAAwB,EAAU;EAC/D,OAAOC,MAAM,CAACD,gBAAgB,CAAC;AACjC","ignoreList":[]}
1
+ {"version":3,"names":["normalizeConfig","config","domain","Error","out","autoReadConsent","undefined","subjectToCOPPA","requestATTBeforeAds","debugEnabled","testMode","normalizeSize","size","split","map","s","trim","filter","length","join","coerceAdUnitId","adUnitIdentifier","String","mapRewardResult","result","earned","type","amount"],"sourceRoot":"../../src","sources":["helpers.ts"],"mappings":";;AAEA,OAAO,SAASA,eAAeA,CAACC,MAAmB,EAAe;EAChE,IAAI,CAACA,MAAM,IAAI,CAACA,MAAM,CAACC,MAAM,EAAE;IAC7B,MAAM,IAAIC,KAAK,CAAC,oDAAoD,CAAC;EACvE;EACA,MAAMC,GAAgB,GAAG;IAAEF,MAAM,EAAED,MAAM,CAACC;EAAO,CAAC;EAClD,IAAID,MAAM,CAACI,eAAe,KAAKC,SAAS,EACtCF,GAAG,CAACC,eAAe,GAAGJ,MAAM,CAACI,eAAe;EAC9C,IAAIJ,MAAM,CAACM,cAAc,KAAKD,SAAS,EACrCF,GAAG,CAACG,cAAc,GAAGN,MAAM,CAACM,cAAc;EAC5C,IAAIN,MAAM,CAACO,mBAAmB,KAAKF,SAAS,EAC1CF,GAAG,CAACI,mBAAmB,GAAGP,MAAM,CAACO,mBAAmB;EACtD,IAAIP,MAAM,CAACQ,YAAY,KAAKH,SAAS,EAAEF,GAAG,CAACK,YAAY,GAAGR,MAAM,CAACQ,YAAY;EAC7E,IAAIR,MAAM,CAACS,QAAQ,KAAKJ,SAAS,EAAEF,GAAG,CAACM,QAAQ,GAAGT,MAAM,CAACS,QAAQ;EACjE,OAAON,GAAG;AACZ;AAEA,OAAO,SAASO,aAAaA,CAACC,IAAwB,EAAU;EAC9D,IAAI,CAACA,IAAI,EAAE,OAAO,EAAE;EACpB,OAAOA,IAAI,CACRC,KAAK,CAAC,GAAG,CAAC,CACVC,GAAG,CAAEC,CAAC,IAAKA,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC,CACpBC,MAAM,CAAEF,CAAC,IAAKA,CAAC,CAACG,MAAM,GAAG,CAAC,CAAC,CAC3BC,IAAI,CAAC,GAAG,CAAC;AACd;AAEA,OAAO,SAASC,cAAcA,CAACC,gBAAwB,EAAU;EAC/D,OAAOC,MAAM,CAACD,gBAAgB,CAAC;AACjC;AAQA;AACA;AACA;AACA;AACA,OAAO,SAASE,eAAeA,CAC7BC,MAA2C,EACF;EACzC,IAAIA,MAAM,IAAIA,MAAM,CAACC,MAAM,EAAE;IAC3B,OAAO;MAAEC,IAAI,EAAEF,MAAM,CAACE,IAAI;MAAEC,MAAM,EAAEH,MAAM,CAACG;IAAO,CAAC;EACrD;EACA,OAAO,IAAI;AACb","ignoreList":[]}
@@ -4,6 +4,7 @@ import NativeEzoicAds from "./NativeEzoicAds.js";
4
4
  import EzoicBannerNative from './EzoicBannerViewNativeComponent';
5
5
  import { coerceAdUnitId, normalizeConfig, normalizeSize } from "./helpers.js";
6
6
  import { jsx as _jsx } from "react/jsx-runtime";
7
+ export { EzoicRewardedAd } from "./EzoicRewardedAd.js";
7
8
  export const EzoicAds = {
8
9
  initialize(config) {
9
10
  return NativeEzoicAds.initialize(normalizeConfig(config));
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEzoicAds","EzoicBannerNative","coerceAdUnitId","normalizeConfig","normalizeSize","jsx","_jsx","EzoicAds","initialize","config","setGDPRConsent","applies","consentString","setGPPConsent","gppString","sectionIds","setSubjectToCOPPA","value","trackPageview","EzoicBannerView","props","adUnitIdentifier","size","onLoad","onError","onImpression","onClick","onOpen","onClose","rest","undefined","e","nativeEvent","onAdClick"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AACA,OAAOA,cAAc,MAA4B,qBAAkB;AACnE,OAAOC,iBAAiB,MAAM,kCAAkC;AAChE,SAASC,cAAc,EAAEC,eAAe,EAAEC,aAAa,QAAQ,cAAW;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAI3E,OAAO,MAAMC,QAAQ,GAAG;EACtBC,UAAUA,CAACC,MAAmB,EAAiB;IAC7C,OAAOT,cAAc,CAACQ,UAAU,CAACL,eAAe,CAACM,MAAM,CAAC,CAAC;EAC3D,CAAC;EACDC,cAAcA,CAACC,OAAgB,EAAEC,aAAsB,EAAQ;IAC7DZ,cAAc,CAACU,cAAc,CAACC,OAAO,EAAEC,aAAa,CAAC;EACvD,CAAC;EACDC,aAAaA,CAACC,SAAkB,EAAEC,UAAmB,EAAQ;IAC3Df,cAAc,CAACa,aAAa,CAACC,SAAS,EAAEC,UAAU,CAAC;EACrD,CAAC;EACDC,iBAAiBA,CAACC,KAAc,EAAQ;IACtCjB,cAAc,CAACgB,iBAAiB,CAACC,KAAK,CAAC;EACzC,CAAC;EACDC,aAAaA,CAAA,EAAqB;IAChC,OAAOlB,cAAc,CAACkB,aAAa,CAAC,CAAC;EACvC;AACF,CAAC;AAmBD,OAAO,SAASC,eAAeA,CAACC,KAA2B,EAAE;EAC3D,MAAM;IACJC,gBAAgB;IAChBC,IAAI;IACJC,MAAM;IACNC,OAAO;IACPC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,OAAO;IACP,GAAGC;EACL,CAAC,GAAGT,KAAK;EACT,oBACEd,IAAA,CAACL,iBAAiB;IAAA,GACZ4B,IAAI;IACRR,gBAAgB,EAAEnB,cAAc,CAACmB,gBAAgB,CAAE;IACnDC,IAAI,EAAElB,aAAa,CAACkB,IAAI,CAAE;IAC1BC,MAAM,EAAEA,MAAM,GAAG,MAAMA,MAAM,CAAC,CAAC,GAAGO,SAAU;IAC5CN,OAAO,EAAEA,OAAO,GAAIO,CAAC,IAAKP,OAAO,CAACO,CAAC,CAACC,WAAW,CAAC,GAAGF,SAAU;IAC7DL,YAAY,EAAEA,YAAY,GAAG,MAAMA,YAAY,CAAC,CAAC,GAAGK,SAAU;IAC9DG,SAAS,EAAEP,OAAO,GAAG,MAAMA,OAAO,CAAC,CAAC,GAAGI,SAAU;IACjDH,MAAM,EAAEA,MAAM,GAAG,MAAMA,MAAM,CAAC,CAAC,GAAGG,SAAU;IAC5CF,OAAO,EAAEA,OAAO,GAAG,MAAMA,OAAO,CAAC,CAAC,GAAGE;EAAU,CAChD,CAAC;AAEN","ignoreList":[]}
1
+ {"version":3,"names":["NativeEzoicAds","EzoicBannerNative","coerceAdUnitId","normalizeConfig","normalizeSize","jsx","_jsx","EzoicRewardedAd","EzoicAds","initialize","config","setGDPRConsent","applies","consentString","setGPPConsent","gppString","sectionIds","setSubjectToCOPPA","value","trackPageview","EzoicBannerView","props","adUnitIdentifier","size","onLoad","onError","onImpression","onClick","onOpen","onClose","rest","undefined","e","nativeEvent","onAdClick"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AACA,OAAOA,cAAc,MAA4B,qBAAkB;AACnE,OAAOC,iBAAiB,MAAM,kCAAkC;AAChE,SAASC,cAAc,EAAEC,eAAe,EAAEC,aAAa,QAAQ,cAAW;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAG3E,SACEC,eAAe,QAGV,sBAAmB;AAE1B,OAAO,MAAMC,QAAQ,GAAG;EACtBC,UAAUA,CAACC,MAAmB,EAAiB;IAC7C,OAAOV,cAAc,CAACS,UAAU,CAACN,eAAe,CAACO,MAAM,CAAC,CAAC;EAC3D,CAAC;EACDC,cAAcA,CAACC,OAAgB,EAAEC,aAAsB,EAAQ;IAC7Db,cAAc,CAACW,cAAc,CAACC,OAAO,EAAEC,aAAa,CAAC;EACvD,CAAC;EACDC,aAAaA,CAACC,SAAkB,EAAEC,UAAmB,EAAQ;IAC3DhB,cAAc,CAACc,aAAa,CAACC,SAAS,EAAEC,UAAU,CAAC;EACrD,CAAC;EACDC,iBAAiBA,CAACC,KAAc,EAAQ;IACtClB,cAAc,CAACiB,iBAAiB,CAACC,KAAK,CAAC;EACzC,CAAC;EACDC,aAAaA,CAAA,EAAqB;IAChC,OAAOnB,cAAc,CAACmB,aAAa,CAAC,CAAC;EACvC;AACF,CAAC;AAmBD,OAAO,SAASC,eAAeA,CAACC,KAA2B,EAAE;EAC3D,MAAM;IACJC,gBAAgB;IAChBC,IAAI;IACJC,MAAM;IACNC,OAAO;IACPC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,OAAO;IACP,GAAGC;EACL,CAAC,GAAGT,KAAK;EACT,oBACEf,IAAA,CAACL,iBAAiB;IAAA,GACZ6B,IAAI;IACRR,gBAAgB,EAAEpB,cAAc,CAACoB,gBAAgB,CAAE;IACnDC,IAAI,EAAEnB,aAAa,CAACmB,IAAI,CAAE;IAC1BC,MAAM,EAAEA,MAAM,GAAG,MAAMA,MAAM,CAAC,CAAC,GAAGO,SAAU;IAC5CN,OAAO,EAAEA,OAAO,GAAIO,CAAC,IAAKP,OAAO,CAACO,CAAC,CAACC,WAAW,CAAC,GAAGF,SAAU;IAC7DL,YAAY,EAAEA,YAAY,GAAG,MAAMA,YAAY,CAAC,CAAC,GAAGK,SAAU;IAC9DG,SAAS,EAAEP,OAAO,GAAG,MAAMA,OAAO,CAAC,CAAC,GAAGI,SAAU;IACjDH,MAAM,EAAEA,MAAM,GAAG,MAAMA,MAAM,CAAC,CAAC,GAAGG,SAAU;IAC5CF,OAAO,EAAEA,OAAO,GAAG,MAAMA,OAAO,CAAC,CAAC,GAAGE;EAAU,CAChD,CAAC;AAEN","ignoreList":[]}
@@ -0,0 +1,54 @@
1
+ /** A reward earned by the user for completing a rewarded ad. */
2
+ export interface EzoicReward {
3
+ type: string;
4
+ amount: number;
5
+ }
6
+ /** Lifecycle callbacks for a rewarded ad. All are optional. */
7
+ export interface EzoicRewardedAdListeners {
8
+ onShown?: () => void;
9
+ onFailedToShow?: (error: {
10
+ message: string;
11
+ }) => void;
12
+ onImpression?: () => void;
13
+ onClicked?: () => void;
14
+ onDismissed?: () => void;
15
+ onUserEarnedReward?: (reward: EzoicReward) => void;
16
+ }
17
+ /**
18
+ * A rewarded ad. Use the static `load` to fetch an ad ahead of time, then call
19
+ * `show()` to present it and grant the reward when the user finishes watching.
20
+ *
21
+ * ```ts
22
+ * const ad = await EzoicRewardedAd.load('12345');
23
+ * ad.setListeners({ onDismissed: () => console.log('closed') });
24
+ * const reward = await ad.show();
25
+ * if (reward) grantReward(reward.amount);
26
+ * ```
27
+ *
28
+ * Mirrors the native `EzoicRewardedAd` load/show lifecycle on both platforms.
29
+ * Rewarded ads are single-use — load a new one for the next opportunity.
30
+ */
31
+ export declare class EzoicRewardedAd {
32
+ /** The Ezoic ad unit identifier this ad was loaded for. */
33
+ readonly adUnitIdentifier: string;
34
+ private listeners;
35
+ private subscription;
36
+ private constructor();
37
+ /**
38
+ * Loads a rewarded ad for the given Ezoic ad unit identifier. Resolves with
39
+ * a ready-to-show `EzoicRewardedAd`, or rejects if no ad could be loaded.
40
+ */
41
+ static load(adUnitIdentifier: string): Promise<EzoicRewardedAd>;
42
+ /** Registers lifecycle callbacks. Replaces any previously set listeners. */
43
+ setListeners(listeners: EzoicRewardedAdListeners): void;
44
+ /**
45
+ * Presents the rewarded ad full-screen. Resolves with the earned reward, or
46
+ * `null` if the ad was dismissed before the reward was earned. Rejects if the
47
+ * ad was not ready (load first) or failed to present.
48
+ */
49
+ show(): Promise<EzoicReward | null>;
50
+ /** Releases the event subscription. Safe to call multiple times. */
51
+ destroy(): void;
52
+ private handleEvent;
53
+ }
54
+ //# sourceMappingURL=EzoicRewardedAd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EzoicRewardedAd.d.ts","sourceRoot":"","sources":["../../../src/EzoicRewardedAd.ts"],"names":[],"mappings":"AAIA,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+DAA+D;AAC/D,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACtD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CACpD;AAuBD;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAe;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAElC,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,YAAY,CAAoC;IAExD,OAAO;IASP;;;OAGG;WACU,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAYrE,4EAA4E;IAC5E,YAAY,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI;IAIvD;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKzC,oEAAoE;IACpE,OAAO,IAAI,IAAI;IAMf,OAAO,CAAC,WAAW;CA2BpB"}
@@ -7,12 +7,27 @@ export interface EzoicConfig {
7
7
  debugEnabled?: boolean;
8
8
  testMode?: boolean;
9
9
  }
10
+ /**
11
+ * Result of `showRewardedAd`. `earned` is true when the user completed the ad
12
+ * and earned the reward; `type`/`amount` describe the granted reward (empty/0
13
+ * when the ad was dismissed without earning). The public `EzoicRewardedAd.show`
14
+ * maps this to `EzoicReward | null`.
15
+ */
16
+ export interface EzoicRewardResult {
17
+ earned: boolean;
18
+ type: string;
19
+ amount: number;
20
+ }
10
21
  export interface Spec extends TurboModule {
11
22
  initialize(config: EzoicConfig): Promise<void>;
12
23
  setGDPRConsent(applies: boolean, consentString?: string): void;
13
24
  setGPPConsent(gppString?: string, sectionIds?: string): void;
14
25
  setSubjectToCOPPA(value: boolean): void;
15
26
  trackPageview(): Promise<boolean>;
27
+ loadRewardedAd(adUnitIdentifier: string): Promise<void>;
28
+ showRewardedAd(adUnitIdentifier: string): Promise<EzoicRewardResult>;
29
+ addListener(eventName: string): void;
30
+ removeListeners(count: number): void;
16
31
  }
17
32
  declare const _default: Spec;
18
33
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeEzoicAds.d.ts","sourceRoot":"","sources":["../../../src/NativeEzoicAds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/D,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7D,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACnC;;AAED,wBAA6E"}
1
+ {"version":3,"file":"NativeEzoicAds.d.ts","sourceRoot":"","sources":["../../../src/NativeEzoicAds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/D,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7D,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAErE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAA6E"}
@@ -2,4 +2,17 @@ import type { EzoicConfig } from './NativeEzoicAds.js';
2
2
  export declare function normalizeConfig(config: EzoicConfig): EzoicConfig;
3
3
  export declare function normalizeSize(size: string | undefined): string;
4
4
  export declare function coerceAdUnitId(adUnitIdentifier: string): string;
5
+ export interface RewardResultLike {
6
+ earned: boolean;
7
+ type: string;
8
+ amount: number;
9
+ }
10
+ /**
11
+ * Maps the native `showRewardedAd` result to the public reward shape: the
12
+ * `{ type, amount }` reward when earned, otherwise `null` (dismissed unearned).
13
+ */
14
+ export declare function mapRewardResult(result: RewardResultLike | null | undefined): {
15
+ type: string;
16
+ amount: number;
17
+ } | null;
5
18
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAkB,CAAC;AAEpD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAchE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO9D;AAED,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE/D"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAkB,CAAC;AAEpD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAchE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO9D;AAED,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzC"}
@@ -1,6 +1,7 @@
1
1
  import type { StyleProp, ViewStyle } from 'react-native';
2
2
  import { type EzoicConfig } from './NativeEzoicAds.js';
3
3
  export type { EzoicConfig };
4
+ export { EzoicRewardedAd, type EzoicReward, type EzoicRewardedAdListeners, } from './EzoicRewardedAd.js';
4
5
  export declare const EzoicAds: {
5
6
  initialize(config: EzoicConfig): Promise<void>;
6
7
  setGDPRConsent(applies: boolean, consentString?: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAuB,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAkB,CAAC;AAIpE,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,eAAO,MAAM,QAAQ;uBACA,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;4BAGtB,OAAO,kBAAkB,MAAM,GAAG,IAAI;8BAGpC,MAAM,eAAe,MAAM,GAAG,IAAI;6BAGnC,OAAO,GAAG,IAAI;qBAGtB,OAAO,CAAC,OAAO,CAAC;CAGlC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAyB1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAuB,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAkB,CAAC;AAIpE,YAAY,EAAE,WAAW,EAAE,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAC9B,MAAM,sBAAmB,CAAC;AAE3B,eAAO,MAAM,QAAQ;uBACA,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;4BAGtB,OAAO,kBAAkB,MAAM,GAAG,IAAI;8BAGpC,MAAM,eAAe,MAAM,GAAG,IAAI;6BAGnC,OAAO,GAAG,IAAI;qBAGtB,OAAO,CAAC,OAAO,CAAC;CAGlC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAyB1D"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ezoic/react-native-sdk",
3
- "version": "1.0.0",
4
- "description": "Ezoic Ads SDK for React Native (Prebid + Google Ad Manager banner ads).",
3
+ "version": "1.1.0",
4
+ "description": "Ezoic Ads SDK for React Native (Prebid + Google Ad Manager banner and rewarded ads).",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
7
7
  "exports": {
@@ -0,0 +1,137 @@
1
+ import { NativeEventEmitter, type EmitterSubscription } from 'react-native';
2
+ import NativeEzoicAds from './NativeEzoicAds';
3
+ import { coerceAdUnitId, mapRewardResult } from './helpers';
4
+
5
+ /** A reward earned by the user for completing a rewarded ad. */
6
+ export interface EzoicReward {
7
+ type: string;
8
+ amount: number;
9
+ }
10
+
11
+ /** Lifecycle callbacks for a rewarded ad. All are optional. */
12
+ export interface EzoicRewardedAdListeners {
13
+ onShown?: () => void;
14
+ onFailedToShow?: (error: { message: string }) => void;
15
+ onImpression?: () => void;
16
+ onClicked?: () => void;
17
+ onDismissed?: () => void;
18
+ onUserEarnedReward?: (reward: EzoicReward) => void;
19
+ }
20
+
21
+ /** The single native event name carrying every rewarded lifecycle signal. */
22
+ const REWARDED_EVENT = 'EzoicRewardedAdEvent';
23
+
24
+ interface RewardedNativeEvent {
25
+ adUnitIdentifier: string;
26
+ type:
27
+ | 'shown'
28
+ | 'failedToShow'
29
+ | 'impression'
30
+ | 'clicked'
31
+ | 'dismissed'
32
+ | 'reward';
33
+ message?: string;
34
+ rewardType?: string;
35
+ rewardAmount?: number;
36
+ }
37
+
38
+ // A single shared emitter is sufficient — events are routed to the right
39
+ // instance by adUnitIdentifier below.
40
+ const emitter = new NativeEventEmitter(NativeEzoicAds as never);
41
+
42
+ /**
43
+ * A rewarded ad. Use the static `load` to fetch an ad ahead of time, then call
44
+ * `show()` to present it and grant the reward when the user finishes watching.
45
+ *
46
+ * ```ts
47
+ * const ad = await EzoicRewardedAd.load('12345');
48
+ * ad.setListeners({ onDismissed: () => console.log('closed') });
49
+ * const reward = await ad.show();
50
+ * if (reward) grantReward(reward.amount);
51
+ * ```
52
+ *
53
+ * Mirrors the native `EzoicRewardedAd` load/show lifecycle on both platforms.
54
+ * Rewarded ads are single-use — load a new one for the next opportunity.
55
+ */
56
+ export class EzoicRewardedAd {
57
+ /** The Ezoic ad unit identifier this ad was loaded for. */
58
+ readonly adUnitIdentifier: string;
59
+
60
+ private listeners: EzoicRewardedAdListeners = {};
61
+ private subscription: EmitterSubscription | null = null;
62
+
63
+ private constructor(adUnitIdentifier: string) {
64
+ this.adUnitIdentifier = adUnitIdentifier;
65
+ this.subscription = emitter.addListener(REWARDED_EVENT, (raw: unknown) => {
66
+ const event = raw as RewardedNativeEvent;
67
+ if (event.adUnitIdentifier !== this.adUnitIdentifier) return;
68
+ this.handleEvent(event);
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Loads a rewarded ad for the given Ezoic ad unit identifier. Resolves with
74
+ * a ready-to-show `EzoicRewardedAd`, or rejects if no ad could be loaded.
75
+ */
76
+ static async load(adUnitIdentifier: string): Promise<EzoicRewardedAd> {
77
+ const id = coerceAdUnitId(adUnitIdentifier);
78
+ const ad = new EzoicRewardedAd(id);
79
+ try {
80
+ await NativeEzoicAds.loadRewardedAd(id);
81
+ return ad;
82
+ } catch (error) {
83
+ ad.destroy();
84
+ throw error;
85
+ }
86
+ }
87
+
88
+ /** Registers lifecycle callbacks. Replaces any previously set listeners. */
89
+ setListeners(listeners: EzoicRewardedAdListeners): void {
90
+ this.listeners = listeners;
91
+ }
92
+
93
+ /**
94
+ * Presents the rewarded ad full-screen. Resolves with the earned reward, or
95
+ * `null` if the ad was dismissed before the reward was earned. Rejects if the
96
+ * ad was not ready (load first) or failed to present.
97
+ */
98
+ async show(): Promise<EzoicReward | null> {
99
+ const result = await NativeEzoicAds.showRewardedAd(this.adUnitIdentifier);
100
+ return mapRewardResult(result);
101
+ }
102
+
103
+ /** Releases the event subscription. Safe to call multiple times. */
104
+ destroy(): void {
105
+ this.subscription?.remove();
106
+ this.subscription = null;
107
+ this.listeners = {};
108
+ }
109
+
110
+ private handleEvent(event: RewardedNativeEvent): void {
111
+ switch (event.type) {
112
+ case 'shown':
113
+ this.listeners.onShown?.();
114
+ break;
115
+ case 'failedToShow':
116
+ this.listeners.onFailedToShow?.({ message: event.message ?? '' });
117
+ break;
118
+ case 'impression':
119
+ this.listeners.onImpression?.();
120
+ break;
121
+ case 'clicked':
122
+ this.listeners.onClicked?.();
123
+ break;
124
+ case 'reward':
125
+ this.listeners.onUserEarnedReward?.({
126
+ type: event.rewardType ?? '',
127
+ amount: event.rewardAmount ?? 0,
128
+ });
129
+ break;
130
+ case 'dismissed':
131
+ this.listeners.onDismissed?.();
132
+ // Dismissal is terminal — the native ad is single-use.
133
+ this.destroy();
134
+ break;
135
+ }
136
+ }
137
+ }
@@ -10,12 +10,29 @@ export interface EzoicConfig {
10
10
  testMode?: boolean;
11
11
  }
12
12
 
13
+ /**
14
+ * Result of `showRewardedAd`. `earned` is true when the user completed the ad
15
+ * and earned the reward; `type`/`amount` describe the granted reward (empty/0
16
+ * when the ad was dismissed without earning). The public `EzoicRewardedAd.show`
17
+ * maps this to `EzoicReward | null`.
18
+ */
19
+ export interface EzoicRewardResult {
20
+ earned: boolean;
21
+ type: string;
22
+ amount: number;
23
+ }
24
+
13
25
  export interface Spec extends TurboModule {
14
26
  initialize(config: EzoicConfig): Promise<void>;
15
27
  setGDPRConsent(applies: boolean, consentString?: string): void;
16
28
  setGPPConsent(gppString?: string, sectionIds?: string): void;
17
29
  setSubjectToCOPPA(value: boolean): void;
18
30
  trackPageview(): Promise<boolean>;
31
+ loadRewardedAd(adUnitIdentifier: string): Promise<void>;
32
+ showRewardedAd(adUnitIdentifier: string): Promise<EzoicRewardResult>;
33
+ // Required by NativeEventEmitter for the rewarded ad lifecycle events.
34
+ addListener(eventName: string): void;
35
+ removeListeners(count: number): void;
19
36
  }
20
37
 
21
38
  export default TurboModuleRegistry.getEnforcing<Spec>('EzoicReactNativeSdk');
package/src/helpers.ts CHANGED
@@ -28,3 +28,22 @@ export function normalizeSize(size: string | undefined): string {
28
28
  export function coerceAdUnitId(adUnitIdentifier: string): string {
29
29
  return String(adUnitIdentifier);
30
30
  }
31
+
32
+ export interface RewardResultLike {
33
+ earned: boolean;
34
+ type: string;
35
+ amount: number;
36
+ }
37
+
38
+ /**
39
+ * Maps the native `showRewardedAd` result to the public reward shape: the
40
+ * `{ type, amount }` reward when earned, otherwise `null` (dismissed unearned).
41
+ */
42
+ export function mapRewardResult(
43
+ result: RewardResultLike | null | undefined
44
+ ): { type: string; amount: number } | null {
45
+ if (result && result.earned) {
46
+ return { type: result.type, amount: result.amount };
47
+ }
48
+ return null;
49
+ }
package/src/index.tsx CHANGED
@@ -4,6 +4,11 @@ import EzoicBannerNative from './EzoicBannerViewNativeComponent';
4
4
  import { coerceAdUnitId, normalizeConfig, normalizeSize } from './helpers';
5
5
 
6
6
  export type { EzoicConfig };
7
+ export {
8
+ EzoicRewardedAd,
9
+ type EzoicReward,
10
+ type EzoicRewardedAdListeners,
11
+ } from './EzoicRewardedAd';
7
12
 
8
13
  export const EzoicAds = {
9
14
  initialize(config: EzoicConfig): Promise<void> {