cloudx-react-native 3.4.6 → 3.6.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 (114) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +16 -0
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdInfo.kt +14 -0
  4. package/android/src/main/java/com/cloudxreactnative/CloudXAdView.kt +238 -0
  5. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +28 -0
  6. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewManagerImpl.kt +54 -0
  7. package/android/src/main/java/com/cloudxreactnative/CloudXExtraParameters.kt +101 -0
  8. package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +171 -57
  9. package/android/src/main/java/com/cloudxreactnative/CloudXPackage.kt +1 -1
  10. package/android/src/newarch/com/cloudxreactnative/CloudXAdViewManager.kt +52 -0
  11. package/android/src/newarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  12. package/android/src/oldarch/com/cloudxreactnative/CloudXAdViewManager.kt +46 -0
  13. package/android/src/oldarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  14. package/cloudx-react-native.podspec +3 -3
  15. package/ios/CloudX.h +2 -2
  16. package/ios/CloudX.mm +268 -62
  17. package/ios/CloudXAdView.h +33 -0
  18. package/ios/CloudXAdView.mm +500 -0
  19. package/ios/CloudXAdViewManager.h +12 -0
  20. package/ios/CloudXAdViewManager.mm +38 -0
  21. package/ios/CloudXExtraParameters.h +27 -0
  22. package/ios/CloudXExtraParameters.mm +117 -0
  23. package/lib/commonjs/CloudX.js +5 -1
  24. package/lib/commonjs/CloudX.js.map +1 -1
  25. package/lib/commonjs/CloudXAdViewBase.js +250 -0
  26. package/lib/commonjs/CloudXAdViewBase.js.map +1 -0
  27. package/lib/commonjs/CloudXAppOpenAd.js +98 -0
  28. package/lib/commonjs/CloudXAppOpenAd.js.map +1 -0
  29. package/lib/commonjs/CloudXBannerView.js +27 -0
  30. package/lib/commonjs/CloudXBannerView.js.map +1 -0
  31. package/lib/commonjs/CloudXMRECView.js +27 -0
  32. package/lib/commonjs/CloudXMRECView.js.map +1 -0
  33. package/lib/commonjs/hooks.js +81 -0
  34. package/lib/commonjs/hooks.js.map +1 -1
  35. package/lib/commonjs/index.js +53 -0
  36. package/lib/commonjs/index.js.map +1 -1
  37. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js +30 -0
  38. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js.map +1 -0
  39. package/lib/commonjs/specs/NativeCloudX.js.map +1 -1
  40. package/lib/commonjs/types/AppOpenAd.js +6 -0
  41. package/lib/commonjs/types/AppOpenAd.js.map +1 -0
  42. package/lib/commonjs/types/RevenueData.js +21 -0
  43. package/lib/commonjs/types/RevenueData.js.map +1 -0
  44. package/lib/commonjs/types/index.js +17 -0
  45. package/lib/commonjs/types/index.js.map +1 -1
  46. package/lib/commonjs/version.js +1 -1
  47. package/lib/module/CloudX.js +5 -1
  48. package/lib/module/CloudX.js.map +1 -1
  49. package/lib/module/CloudXAdViewBase.js +242 -0
  50. package/lib/module/CloudXAdViewBase.js.map +1 -0
  51. package/lib/module/CloudXAppOpenAd.js +91 -0
  52. package/lib/module/CloudXAppOpenAd.js.map +1 -0
  53. package/lib/module/CloudXBannerView.js +19 -0
  54. package/lib/module/CloudXBannerView.js.map +1 -0
  55. package/lib/module/CloudXMRECView.js +19 -0
  56. package/lib/module/CloudXMRECView.js.map +1 -0
  57. package/lib/module/hooks.js +80 -0
  58. package/lib/module/hooks.js.map +1 -1
  59. package/lib/module/index.js +10 -1
  60. package/lib/module/index.js.map +1 -1
  61. package/lib/module/specs/CloudXAdViewNativeComponent.js +27 -0
  62. package/lib/module/specs/CloudXAdViewNativeComponent.js.map +1 -0
  63. package/lib/module/specs/NativeCloudX.js.map +1 -1
  64. package/lib/module/types/AppOpenAd.js +2 -0
  65. package/lib/module/types/AppOpenAd.js.map +1 -0
  66. package/lib/module/types/RevenueData.js +15 -0
  67. package/lib/module/types/RevenueData.js.map +1 -0
  68. package/lib/module/types/index.js +1 -1
  69. package/lib/module/types/index.js.map +1 -1
  70. package/lib/module/version.js +1 -1
  71. package/lib/typescript/src/CloudX.d.ts.map +1 -1
  72. package/lib/typescript/src/CloudXAdViewBase.d.ts +28 -0
  73. package/lib/typescript/src/CloudXAdViewBase.d.ts.map +1 -0
  74. package/lib/typescript/src/CloudXAppOpenAd.d.ts +4 -0
  75. package/lib/typescript/src/CloudXAppOpenAd.d.ts.map +1 -0
  76. package/lib/typescript/src/CloudXBannerView.d.ts +16 -0
  77. package/lib/typescript/src/CloudXBannerView.d.ts.map +1 -0
  78. package/lib/typescript/src/CloudXMRECView.d.ts +16 -0
  79. package/lib/typescript/src/CloudXMRECView.d.ts.map +1 -0
  80. package/lib/typescript/src/hooks.d.ts +14 -0
  81. package/lib/typescript/src/hooks.d.ts.map +1 -1
  82. package/lib/typescript/src/index.d.ts +9 -3
  83. package/lib/typescript/src/index.d.ts.map +1 -1
  84. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts +62 -0
  85. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts.map +1 -0
  86. package/lib/typescript/src/specs/NativeCloudX.d.ts +13 -0
  87. package/lib/typescript/src/specs/NativeCloudX.d.ts.map +1 -1
  88. package/lib/typescript/src/types/AdProps.d.ts +3 -0
  89. package/lib/typescript/src/types/AdProps.d.ts.map +1 -1
  90. package/lib/typescript/src/types/AppOpenAd.d.ts +8 -0
  91. package/lib/typescript/src/types/AppOpenAd.d.ts.map +1 -0
  92. package/lib/typescript/src/types/CloudX.d.ts +6 -0
  93. package/lib/typescript/src/types/CloudX.d.ts.map +1 -1
  94. package/lib/typescript/src/types/RevenueData.d.ts +32 -0
  95. package/lib/typescript/src/types/RevenueData.d.ts.map +1 -0
  96. package/lib/typescript/src/types/index.d.ts +3 -0
  97. package/lib/typescript/src/types/index.d.ts.map +1 -1
  98. package/lib/typescript/src/version.d.ts +1 -1
  99. package/package.json +6 -2
  100. package/src/CloudX.ts +6 -0
  101. package/src/CloudXAdViewBase.tsx +294 -0
  102. package/src/CloudXAppOpenAd.ts +109 -0
  103. package/src/CloudXBannerView.tsx +20 -0
  104. package/src/CloudXMRECView.tsx +20 -0
  105. package/src/hooks.ts +103 -0
  106. package/src/index.ts +22 -2
  107. package/src/specs/CloudXAdViewNativeComponent.ts +84 -0
  108. package/src/specs/NativeCloudX.ts +22 -0
  109. package/src/types/AdProps.ts +4 -0
  110. package/src/types/AppOpenAd.ts +8 -0
  111. package/src/types/CloudX.ts +7 -0
  112. package/src/types/RevenueData.ts +39 -0
  113. package/src/types/index.ts +3 -0
  114. package/src/version.ts +1 -1
@@ -0,0 +1,500 @@
1
+ //
2
+ // CloudXAdView.mm
3
+ // cloudx-react-native
4
+ //
5
+
6
+ #import "CloudXAdView.h"
7
+ #import "CloudXExtraParameters.h"
8
+ #import <React/RCTLog.h>
9
+
10
+ #ifdef RCT_NEW_ARCH_ENABLED
11
+ #import <react/renderer/components/CloudXSpec/ComponentDescriptors.h>
12
+ #import <react/renderer/components/CloudXSpec/EventEmitters.h>
13
+ #import <react/renderer/components/CloudXSpec/Props.h>
14
+ #import <react/renderer/components/CloudXSpec/RCTComponentViewHelpers.h>
15
+ #import <React/RCTConversions.h>
16
+ #import "RCTFabricComponentsPlugins.h"
17
+
18
+ using namespace facebook::react;
19
+
20
+ /*
21
+ * Fills a generated AdInfo-style event struct from the NSDictionary the ad
22
+ * delegate builds. All three ad-info events share the same field set, so this
23
+ * macro keeps the three emitter blocks in sync.
24
+ */
25
+ #define CLX_FILL_ADINFO(result, event) \
26
+ result.adUnitId = std::string([(event[@"adUnitId"] ?: @"") UTF8String]); \
27
+ result.adFormat = std::string([(event[@"adFormat"] ?: @"") UTF8String]); \
28
+ result.networkName = std::string([(event[@"networkName"] ?: @"") UTF8String]); \
29
+ result.networkPlacement = std::string([(event[@"networkPlacement"] ?: @"") UTF8String]); \
30
+ result.placement = std::string([(event[@"placement"] ?: @"") UTF8String]); \
31
+ result.revenue = [event[@"revenue"] doubleValue]; \
32
+ result.adValuesJson = std::string([(event[@"adValuesJson"] ?: @"") UTF8String]);
33
+
34
+ static id CLXIdFromFollyDynamic(const folly::dynamic &dynamicValue)
35
+ {
36
+ switch ( dynamicValue.type() )
37
+ {
38
+ case folly::dynamic::NULLT:
39
+ return [NSNull null];
40
+ case folly::dynamic::BOOL:
41
+ return @(dynamicValue.getBool());
42
+ case folly::dynamic::INT64:
43
+ return @(dynamicValue.getInt());
44
+ case folly::dynamic::DOUBLE:
45
+ return @(dynamicValue.getDouble());
46
+ case folly::dynamic::STRING:
47
+ return [[NSString alloc] initWithBytes: dynamicValue.c_str() length: dynamicValue.size() encoding: NSUTF8StringEncoding] ?: @"";
48
+ case folly::dynamic::ARRAY: {
49
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity: dynamicValue.size()];
50
+ for ( const auto &item : dynamicValue )
51
+ {
52
+ [array addObject: CLXIdFromFollyDynamic(item) ?: [NSNull null]];
53
+ }
54
+ return [array copy];
55
+ }
56
+ case folly::dynamic::OBJECT: {
57
+ NSMutableDictionary<NSString *, id> *dictionary = [NSMutableDictionary dictionaryWithCapacity: dynamicValue.size()];
58
+ for ( const auto &item : dynamicValue.items() )
59
+ {
60
+ id key = CLXIdFromFollyDynamic(item.first);
61
+ if ( ![key isKindOfClass: [NSString class]] ) continue;
62
+ dictionary[key] = CLXIdFromFollyDynamic(item.second) ?: [NSNull null];
63
+ }
64
+ return [dictionary copy];
65
+ }
66
+ }
67
+
68
+ return [NSNull null];
69
+ }
70
+
71
+ @interface CloudXAdView () <RCTCloudXAdViewViewProtocol>
72
+ #else
73
+ @interface CloudXAdView ()
74
+ #endif
75
+
76
+ @property (nonatomic, strong, nullable) CLXBannerAdView *adView;
77
+ @property (nonatomic, copy, nullable) NSString *adUnitId;
78
+ @property (nonatomic, copy, nullable) NSString *adFormat;
79
+ @property (nonatomic, copy, nullable) NSString *placement;
80
+ @property (nonatomic, copy, nullable) NSString *customData;
81
+ @property (nonatomic, copy) NSDictionary<NSString *, id> *extraParameters;
82
+ @property (nonatomic, copy) NSSet<NSString *> *appliedExtraParameterKeys;
83
+
84
+ @end
85
+
86
+ @implementation CloudXAdView
87
+
88
+ - (void)commonInit
89
+ {
90
+ _extraParameters = @{};
91
+ _appliedExtraParameterKeys = [NSSet set];
92
+ }
93
+
94
+ #ifdef RCT_NEW_ARCH_ENABLED
95
+
96
+ + (ComponentDescriptorProvider)componentDescriptorProvider
97
+ {
98
+ return concreteComponentDescriptorProvider<CloudXAdViewComponentDescriptor>();
99
+ }
100
+
101
+ - (instancetype)initWithFrame:(CGRect)frame
102
+ {
103
+ self = [super initWithFrame: frame];
104
+ if ( self )
105
+ {
106
+ [self commonInit];
107
+ static const auto defaultProps = std::make_shared<const CloudXAdViewProps>();
108
+ _props = defaultProps;
109
+ [self setupEventHandlers];
110
+ }
111
+ return self;
112
+ }
113
+
114
+ /*
115
+ * On Fabric the RCTDirectEventBlock properties are not wired by a view manager,
116
+ * so point each block at the generated C++ EventEmitter. The delegate callbacks
117
+ * below always call self.onXxxEvent(dict) regardless of architecture.
118
+ */
119
+ - (void)setupEventHandlers
120
+ {
121
+ __weak CloudXAdView *weakSelf = self;
122
+
123
+ self.onAdLoadedEvent = ^(NSDictionary *event) {
124
+ CloudXAdView *strongSelf = weakSelf;
125
+ if ( !strongSelf || !strongSelf->_eventEmitter ) return;
126
+ auto emitter = std::static_pointer_cast<CloudXAdViewEventEmitter const>(strongSelf->_eventEmitter);
127
+ CloudXAdViewEventEmitter::OnAdLoadedEvent result;
128
+ CLX_FILL_ADINFO(result, event);
129
+ emitter->onAdLoadedEvent(result);
130
+ };
131
+
132
+ self.onAdClickedEvent = ^(NSDictionary *event) {
133
+ CloudXAdView *strongSelf = weakSelf;
134
+ if ( !strongSelf || !strongSelf->_eventEmitter ) return;
135
+ auto emitter = std::static_pointer_cast<CloudXAdViewEventEmitter const>(strongSelf->_eventEmitter);
136
+ CloudXAdViewEventEmitter::OnAdClickedEvent result;
137
+ CLX_FILL_ADINFO(result, event);
138
+ emitter->onAdClickedEvent(result);
139
+ };
140
+
141
+ self.onAdRevenuePaidEvent = ^(NSDictionary *event) {
142
+ CloudXAdView *strongSelf = weakSelf;
143
+ if ( !strongSelf || !strongSelf->_eventEmitter ) return;
144
+ auto emitter = std::static_pointer_cast<CloudXAdViewEventEmitter const>(strongSelf->_eventEmitter);
145
+ CloudXAdViewEventEmitter::OnAdRevenuePaidEvent result;
146
+ CLX_FILL_ADINFO(result, event);
147
+ emitter->onAdRevenuePaidEvent(result);
148
+ };
149
+
150
+ self.onAdLoadFailedEvent = ^(NSDictionary *event) {
151
+ CloudXAdView *strongSelf = weakSelf;
152
+ if ( !strongSelf || !strongSelf->_eventEmitter ) return;
153
+ auto emitter = std::static_pointer_cast<CloudXAdViewEventEmitter const>(strongSelf->_eventEmitter);
154
+ CloudXAdViewEventEmitter::OnAdLoadFailedEvent result;
155
+ result.adUnitId = std::string([(event[@"adUnitId"] ?: @"") UTF8String]);
156
+ result.code = [event[@"code"] doubleValue];
157
+ result.message = std::string([(event[@"message"] ?: @"") UTF8String]);
158
+ emitter->onAdLoadFailedEvent(result);
159
+ };
160
+ }
161
+
162
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
163
+ {
164
+ const auto &oldViewProps = *std::static_pointer_cast<CloudXAdViewProps const>(_props);
165
+ const auto &newViewProps = *std::static_pointer_cast<CloudXAdViewProps const>(props);
166
+
167
+ BOOL shouldAttach = NO;
168
+
169
+ if ( oldViewProps.adUnitId != newViewProps.adUnitId )
170
+ {
171
+ [self setAdUnitId: RCTNSStringFromString(newViewProps.adUnitId)];
172
+ shouldAttach = YES;
173
+ }
174
+ if ( oldViewProps.adFormat != newViewProps.adFormat )
175
+ {
176
+ [self setAdFormat: RCTNSStringFromString(newViewProps.adFormat)];
177
+ }
178
+ if ( oldViewProps.placement != newViewProps.placement )
179
+ {
180
+ [self setPlacement: RCTNSStringFromStringNilIfEmpty(newViewProps.placement)];
181
+ }
182
+ if ( oldViewProps.customData != newViewProps.customData )
183
+ {
184
+ [self setCustomData: RCTNSStringFromStringNilIfEmpty(newViewProps.customData)];
185
+ }
186
+ /*
187
+ * CloudXAdViewExtraParametersStruct contains folly::dynamic, so the
188
+ * generated props do not support a simple old/new equality guard like the
189
+ * scalar props above. Applying is idempotent, with removed keys cleared by
190
+ * applyExtraParametersIfReady.
191
+ */
192
+ [self setExtraParametersFromEntryArray: [self entryArrayFromFabricExtraParameters: newViewProps.extraParameters]];
193
+
194
+ if ( shouldAttach )
195
+ {
196
+ [self attachAdViewIfNeeded];
197
+ }
198
+
199
+ [super updateProps: props oldProps: oldProps];
200
+ }
201
+
202
+ - (void)prepareForRecycle
203
+ {
204
+ [super prepareForRecycle];
205
+
206
+ static const auto defaultProps = std::make_shared<const CloudXAdViewProps>();
207
+ _props = defaultProps;
208
+
209
+ [self destroyAdIfNeeded];
210
+ self.adUnitId = nil;
211
+ self.adFormat = nil;
212
+ self.placement = nil;
213
+ self.customData = nil;
214
+ self.extraParameters = @{};
215
+ self.appliedExtraParameterKeys = [NSSet set];
216
+ }
217
+
218
+ - (NSArray<NSDictionary<NSString *, id> *> *)entryArrayFromFabricExtraParameters:(const std::vector<CloudXAdViewExtraParametersStruct> &)entries
219
+ {
220
+ NSMutableArray<NSDictionary<NSString *, id> *> *result = [NSMutableArray arrayWithCapacity: entries.size()];
221
+ for ( const auto &entry : entries )
222
+ {
223
+ NSString *key = [NSString stringWithUTF8String: entry.key.c_str()] ?: @"";
224
+ id value = CLXIdFromFollyDynamic(entry.value) ?: [NSNull null];
225
+ [result addObject: @{@"key": key, @"value": value}];
226
+ }
227
+ return [result copy];
228
+ }
229
+
230
+ #else
231
+
232
+ - (instancetype)init
233
+ {
234
+ self = [super init];
235
+ if ( self )
236
+ {
237
+ [self commonInit];
238
+ }
239
+ return self;
240
+ }
241
+
242
+ #endif // RCT_NEW_ARCH_ENABLED
243
+
244
+ #pragma mark - Property setters (shared; Paper view manager sets these via KVC)
245
+
246
+ - (void)setAdUnitId:(NSString *)adUnitId
247
+ {
248
+ if ( self.adView )
249
+ {
250
+ // Ad unit ID is immutable once the ad view is created. Warn only on an
251
+ // actual change; unchanged re-sets on re-render stay quiet.
252
+ if ( ![adUnitId isEqualToString: _adUnitId] )
253
+ {
254
+ RCTLogWarn(@"CloudXAdView: adUnitId cannot change after the ad view is created; remount the component (e.g. via a React key).");
255
+ }
256
+ return;
257
+ }
258
+ _adUnitId = adUnitId;
259
+ }
260
+
261
+ - (void)setAdFormat:(NSString *)adFormat
262
+ {
263
+ if ( self.adView )
264
+ {
265
+ if ( ![adFormat isEqualToString: _adFormat] )
266
+ {
267
+ RCTLogWarn(@"CloudXAdView: adFormat cannot change after the ad view is created; remount the component (e.g. via a React key).");
268
+ }
269
+ return;
270
+ }
271
+ _adFormat = adFormat;
272
+ }
273
+
274
+ - (void)setPlacement:(NSString *)placement
275
+ {
276
+ _placement = placement;
277
+ self.adView.placement = placement;
278
+ }
279
+
280
+ - (void)setCustomData:(NSString *)customData
281
+ {
282
+ _customData = customData;
283
+ self.adView.customData = customData;
284
+ }
285
+
286
+ - (void)setExtraParametersFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)extraParameters
287
+ {
288
+ self.extraParameters = [CloudXExtraParameters dictionaryFromEntryArray: extraParameters];
289
+ [self applyExtraParametersIfReady];
290
+ }
291
+
292
+ #pragma mark - Paper attach hook
293
+
294
+ // Invoked after all JS props are set when mounting on the Old Architecture.
295
+ - (void)didSetProps:(NSArray<NSString *> *)changedProps
296
+ {
297
+ [self attachAdViewIfNeeded];
298
+ }
299
+
300
+ #pragma mark - Ad view lifecycle
301
+
302
+ - (void)attachAdViewIfNeeded
303
+ {
304
+ NSString *adUnitId = self.adUnitId;
305
+ NSString *adFormat = self.adFormat;
306
+
307
+ dispatch_async(dispatch_get_main_queue(), ^{
308
+ if ( self.adView ) return;
309
+ if ( adUnitId.length == 0 || adFormat.length == 0 ) return;
310
+
311
+ CLXBannerAdView *adView;
312
+ if ( [adFormat caseInsensitiveCompare: @"MREC"] == NSOrderedSame )
313
+ {
314
+ adView = [[CloudXCore shared] createMRECWithAdUnitId: adUnitId];
315
+ }
316
+ else
317
+ {
318
+ adView = [[CloudXCore shared] createBannerWithAdUnitId: adUnitId];
319
+ }
320
+
321
+ if ( !adView )
322
+ {
323
+ /*
324
+ * createBanner/createMRECWithAdUnitId: returned nil. The JS layer
325
+ * gates mounting on SDK initialization, so a nil here post-init is
326
+ * an invalid/expired ad unit — surface it as a load failure instead
327
+ * of leaving a permanently-dead, silent view (self.adView stays nil
328
+ * and props don't re-land, so there is no retry).
329
+ */
330
+ if ( self.onAdLoadFailedEvent )
331
+ {
332
+ self.onAdLoadFailedEvent(@{@"adUnitId": adUnitId ?: @"",
333
+ @"code": @(CLXErrorCodeInvalidAdUnit),
334
+ @"message": @"Ad view creation returned nil (invalid ad unit or SDK not initialized)"});
335
+ }
336
+ return;
337
+ }
338
+
339
+ adView.delegate = self;
340
+ adView.revenueDelegate = self;
341
+ adView.placement = self.placement;
342
+ adView.customData = self.customData;
343
+ adView.userInteractionEnabled = YES;
344
+ adView.translatesAutoresizingMaskIntoConstraints = NO;
345
+
346
+ self.adView = adView;
347
+ [self applyExtraParametersIfReady];
348
+ [self addSubview: adView];
349
+ [NSLayoutConstraint activateConstraints: @[
350
+ [adView.topAnchor constraintEqualToAnchor: self.topAnchor],
351
+ [adView.bottomAnchor constraintEqualToAnchor: self.bottomAnchor],
352
+ [adView.leadingAnchor constraintEqualToAnchor: self.leadingAnchor],
353
+ [adView.trailingAnchor constraintEqualToAnchor: self.trailingAnchor],
354
+ ]];
355
+
356
+ [adView load];
357
+ [self updateAutoRefreshForWindowVisibility];
358
+ });
359
+ }
360
+
361
+ - (void)updateAutoRefreshForWindowVisibility
362
+ {
363
+ if ( !self.adView ) return;
364
+ if ( self.window )
365
+ {
366
+ [self.adView startAutoRefresh];
367
+ }
368
+ else
369
+ {
370
+ [self.adView stopAutoRefresh];
371
+ }
372
+ }
373
+
374
+ - (void)didMoveToWindow
375
+ {
376
+ [super didMoveToWindow];
377
+ [self updateAutoRefreshForWindowVisibility];
378
+ }
379
+
380
+ - (void)didMoveToSuperview
381
+ {
382
+ [super didMoveToSuperview];
383
+
384
+ if ( self.superview != nil )
385
+ {
386
+ [self updateAutoRefreshForWindowVisibility];
387
+ return;
388
+ }
389
+
390
+ // Superview nil could be a real unmount or brief reparenting (react-native-screens).
391
+ // Defer one main-queue turn; destroy only if it is still detached. Strong ref so the
392
+ // ad is always torn down on a genuine unmount.
393
+ CloudXAdView *viewRef = self;
394
+ dispatch_async(dispatch_get_main_queue(), ^{
395
+ if ( viewRef.superview != nil )
396
+ {
397
+ [viewRef updateAutoRefreshForWindowVisibility];
398
+ return;
399
+ }
400
+ [viewRef destroyAdIfNeeded];
401
+ });
402
+ }
403
+
404
+ - (void)dealloc
405
+ {
406
+ [self destroyAdIfNeeded];
407
+ }
408
+
409
+ - (void)destroyAdIfNeeded
410
+ {
411
+ if ( !self.adView ) return;
412
+
413
+ [self.adView stopAutoRefresh];
414
+ self.adView.delegate = nil;
415
+ self.adView.revenueDelegate = nil;
416
+ [self.adView destroy];
417
+ [self.adView removeFromSuperview];
418
+ self.adView = nil;
419
+ self.appliedExtraParameterKeys = [NSSet set];
420
+ }
421
+
422
+ - (void)applyExtraParametersIfReady
423
+ {
424
+ if ( !self.adView ) return;
425
+
426
+ NSDictionary<NSString *, id> *extraParameters = self.extraParameters ?: @{};
427
+ [CloudXExtraParameters applyExtraParameters: extraParameters toAdView: self.adView logBlock: nil];
428
+
429
+ NSMutableDictionary<NSString *, id> *removed = [NSMutableDictionary dictionary];
430
+ for ( NSString *key in self.appliedExtraParameterKeys )
431
+ {
432
+ if ( !extraParameters[key] )
433
+ {
434
+ removed[key] = [NSNull null];
435
+ }
436
+ }
437
+ [CloudXExtraParameters applyExtraParameters: removed toAdView: self.adView logBlock: nil];
438
+ self.appliedExtraParameterKeys = [NSSet setWithArray: extraParameters.allKeys];
439
+ }
440
+
441
+ #pragma mark - Event payloads
442
+
443
+ - (NSDictionary<NSString *, id> *)adInfoDictForAd:(CLXAd *)ad
444
+ {
445
+ NSString *adValuesJson = @"";
446
+ if ( ad.adValues )
447
+ {
448
+ NSData *data = [NSJSONSerialization dataWithJSONObject: ad.adValues options: 0 error: nil];
449
+ if ( data )
450
+ {
451
+ adValuesJson = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] ?: @"";
452
+ }
453
+ }
454
+
455
+ return @{@"adUnitId": ad.adUnitId ?: @"",
456
+ @"adFormat": self.adFormat ?: @"",
457
+ @"networkName": ad.networkName ?: @"",
458
+ @"networkPlacement": ad.networkPlacement ?: @"",
459
+ @"placement": ad.placement ?: @"",
460
+ @"revenue": ad.revenue ?: @(0),
461
+ @"adValuesJson": adValuesJson};
462
+ }
463
+
464
+ - (NSDictionary<NSString *, id> *)loadFailedDictForAdUnit:(NSString *)adUnitId error:(CLXError *)error
465
+ {
466
+ return @{@"adUnitId": adUnitId ?: (self.adUnitId ?: @""),
467
+ @"code": error ? @(error.code) : @(CLXErrorCodeInternalError),
468
+ @"message": error.localizedDescription ?: @""};
469
+ }
470
+
471
+ #pragma mark - CLXBannerDelegate / CLXAdRevenueDelegate
472
+
473
+ - (void)didLoadAd:(CLXAd *)ad
474
+ {
475
+ if ( self.onAdLoadedEvent ) self.onAdLoadedEvent([self adInfoDictForAd: ad]);
476
+ }
477
+
478
+ - (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error
479
+ {
480
+ if ( self.onAdLoadFailedEvent ) self.onAdLoadFailedEvent([self loadFailedDictForAdUnit: adUnitId error: error]);
481
+ }
482
+
483
+ - (void)didClickAd:(CLXAd *)ad
484
+ {
485
+ if ( self.onAdClickedEvent ) self.onAdClickedEvent([self adInfoDictForAd: ad]);
486
+ }
487
+
488
+ - (void)didPayRevenueForAd:(CLXAd *)ad
489
+ {
490
+ if ( self.onAdRevenuePaidEvent ) self.onAdRevenuePaidEvent([self adInfoDictForAd: ad]);
491
+ }
492
+
493
+ @end
494
+
495
+ #ifdef RCT_NEW_ARCH_ENABLED
496
+ Class<RCTComponentViewProtocol> CloudXAdViewCls(void)
497
+ {
498
+ return [CloudXAdView class];
499
+ }
500
+ #endif
@@ -0,0 +1,12 @@
1
+ //
2
+ // CloudXAdViewManager.h
3
+ // cloudx-react-native
4
+ //
5
+ // Old-Architecture (Paper) view manager for the in-tree CloudXAdView.
6
+ //
7
+
8
+ #import <React/RCTViewManager.h>
9
+
10
+ @interface CloudXAdViewManager : RCTViewManager
11
+
12
+ @end
@@ -0,0 +1,38 @@
1
+ //
2
+ // CloudXAdViewManager.mm
3
+ // cloudx-react-native
4
+ //
5
+
6
+ #import "CloudXAdViewManager.h"
7
+ #import "CloudXAdView.h"
8
+
9
+ @implementation CloudXAdViewManager
10
+
11
+ RCT_EXPORT_MODULE(CloudXAdView)
12
+
13
+ RCT_EXPORT_VIEW_PROPERTY(adUnitId, NSString)
14
+ RCT_EXPORT_VIEW_PROPERTY(adFormat, NSString)
15
+ RCT_EXPORT_VIEW_PROPERTY(placement, NSString)
16
+ RCT_EXPORT_VIEW_PROPERTY(customData, NSString)
17
+ RCT_CUSTOM_VIEW_PROPERTY(extraParameters, NSArray, CloudXAdView)
18
+ {
19
+ NSArray<NSDictionary<NSString *, id> *> *entries = [json isKindOfClass: [NSArray class]] ? json : nil;
20
+ [view setExtraParametersFromEntryArray: entries];
21
+ }
22
+
23
+ RCT_EXPORT_VIEW_PROPERTY(onAdLoadedEvent, RCTDirectEventBlock)
24
+ RCT_EXPORT_VIEW_PROPERTY(onAdLoadFailedEvent, RCTDirectEventBlock)
25
+ RCT_EXPORT_VIEW_PROPERTY(onAdClickedEvent, RCTDirectEventBlock)
26
+ RCT_EXPORT_VIEW_PROPERTY(onAdRevenuePaidEvent, RCTDirectEventBlock)
27
+
28
+ + (BOOL)requiresMainQueueSetup
29
+ {
30
+ return YES;
31
+ }
32
+
33
+ - (UIView *)view
34
+ {
35
+ return [[CloudXAdView alloc] init];
36
+ }
37
+
38
+ @end
@@ -0,0 +1,27 @@
1
+ /*
2
+ * CloudXExtraParameters.h
3
+ * cloudx-react-native
4
+ */
5
+
6
+ #import <Foundation/Foundation.h>
7
+ #import <CloudXCore/CloudXCore.h>
8
+
9
+ NS_ASSUME_NONNULL_BEGIN
10
+
11
+ typedef void (^CloudXExtraParametersLogBlock)(NSString *message);
12
+
13
+ @interface CloudXExtraParameters : NSObject
14
+
15
+ + (NSDictionary<NSString *, id> *)dictionaryFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)entries;
16
+ + (void)mergeExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
17
+ intoPendingDictionary:(NSMutableDictionary<NSString *, id> *)pending;
18
+ + (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
19
+ toAdView:(CLXBannerAdView *)adView
20
+ logBlock:(nullable CloudXExtraParametersLogBlock)logBlock;
21
+ + (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
22
+ toFullscreenAd:(id<CLXFullscreenAd>)ad
23
+ logBlock:(nullable CloudXExtraParametersLogBlock)logBlock;
24
+
25
+ @end
26
+
27
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,117 @@
1
+ /*
2
+ * CloudXExtraParameters.mm
3
+ * cloudx-react-native
4
+ */
5
+
6
+ #import "CloudXExtraParameters.h"
7
+
8
+ @implementation CloudXExtraParameters
9
+
10
+ + (NSDictionary<NSString *, id> *)dictionaryFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)entries
11
+ {
12
+ if ( !entries || ![(id)entries isKindOfClass: [NSArray class]] )
13
+ {
14
+ return @{};
15
+ }
16
+
17
+ NSMutableDictionary<NSString *, id> *extraParameters = [NSMutableDictionary dictionary];
18
+ for ( id entry in entries )
19
+ {
20
+ if ( ![entry isKindOfClass: [NSDictionary class]] ) continue;
21
+
22
+ NSDictionary<NSString *, id> *entryDictionary = (NSDictionary<NSString *, id> *)entry;
23
+ id keyValue = entryDictionary[@"key"];
24
+ if ( ![keyValue isKindOfClass: [NSString class]] ) continue;
25
+
26
+ NSString *key = (NSString *)keyValue;
27
+ if ( key.length == 0 ) continue;
28
+
29
+ extraParameters[key] = entryDictionary[@"value"] ?: [NSNull null];
30
+ }
31
+
32
+ return [extraParameters copy];
33
+ }
34
+
35
+ + (void)mergeExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
36
+ intoPendingDictionary:(NSMutableDictionary<NSString *, id> *)pending
37
+ {
38
+ [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
39
+ if ( !key || key.length == 0 ) return;
40
+ if ( !value || [value isKindOfClass: [NSNull class]] )
41
+ {
42
+ [pending removeObjectForKey: key];
43
+ }
44
+ else
45
+ {
46
+ pending[key] = value;
47
+ }
48
+ }];
49
+ }
50
+
51
+ + (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
52
+ toAdView:(CLXBannerAdView *)adView
53
+ logBlock:(nullable CloudXExtraParametersLogBlock)logBlock
54
+ {
55
+ SEL selector = NSSelectorFromString(@"setExtraParameter:value:");
56
+ id target = [adView respondsToSelector: selector] ? adView : adView.ad;
57
+
58
+ [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
59
+ if ( !key || key.length == 0 ) return;
60
+ id normalizedValue = [value isKindOfClass: [NSNull class]] ? nil : value;
61
+ [self applyExtraParameterValue: normalizedValue forKey: key toTarget: target logBlock: logBlock];
62
+ }];
63
+ }
64
+
65
+ + (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters
66
+ toFullscreenAd:(id<CLXFullscreenAd>)ad
67
+ logBlock:(nullable CloudXExtraParametersLogBlock)logBlock
68
+ {
69
+ [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
70
+ if ( !key || key.length == 0 ) return;
71
+ id normalizedValue = [value isKindOfClass: [NSNull class]] ? nil : value;
72
+ [self applyExtraParameterValue: normalizedValue forKey: key toTarget: ad logBlock: logBlock];
73
+ }];
74
+ }
75
+
76
+ + (void)applyExtraParameterValue:(id)value
77
+ forKey:(NSString *)key
78
+ toTarget:(id)target
79
+ logBlock:(nullable CloudXExtraParametersLogBlock)logBlock
80
+ {
81
+ if ( !target || !key || key.length == 0 ) return;
82
+
83
+ SEL selector = NSSelectorFromString(@"setExtraParameter:value:");
84
+ if ( ![target respondsToSelector: selector] )
85
+ {
86
+ selector = NSSelectorFromString(@"setExtraParameterForKey:value:");
87
+ }
88
+
89
+ if ( ![target respondsToSelector: selector] )
90
+ {
91
+ if ( logBlock )
92
+ {
93
+ logBlock(
94
+ [NSString stringWithFormat: @"Skipping extra parameter \"%@\" because %@ does not respond to setExtraParameter:value: or setExtraParameterForKey:value:",
95
+ key,
96
+ NSStringFromClass([target class])]
97
+ );
98
+ }
99
+ return;
100
+ }
101
+
102
+ using SetExtraParameterMethod = void (*)(id, SEL, NSString *, id);
103
+ SetExtraParameterMethod method = (SetExtraParameterMethod)[target methodForSelector: selector];
104
+ method(target, selector, key, value);
105
+
106
+ if ( logBlock )
107
+ {
108
+ logBlock(
109
+ [NSString stringWithFormat: @"Applied extra parameter \"%@\" with value \"%@\" to %@",
110
+ key,
111
+ value ?: @"(cleared)",
112
+ NSStringFromClass([target class])]
113
+ );
114
+ }
115
+ }
116
+
117
+ @end