cloudx-react-native 3.4.5 → 3.4.7

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 (63) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +10 -0
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdView.kt +244 -0
  4. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +28 -0
  5. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewManagerImpl.kt +54 -0
  6. package/android/src/main/java/com/cloudxreactnative/CloudXExtraParameters.kt +101 -0
  7. package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +5 -57
  8. package/android/src/main/java/com/cloudxreactnative/CloudXPackage.kt +1 -1
  9. package/android/src/newarch/com/cloudxreactnative/CloudXAdViewManager.kt +52 -0
  10. package/android/src/oldarch/com/cloudxreactnative/CloudXAdViewManager.kt +46 -0
  11. package/ios/CloudX.mm +13 -57
  12. package/ios/CloudXAdView.h +35 -0
  13. package/ios/CloudXAdView.mm +505 -0
  14. package/ios/CloudXAdViewManager.h +12 -0
  15. package/ios/CloudXAdViewManager.mm +38 -0
  16. package/ios/CloudXExtraParameters.h +27 -0
  17. package/ios/CloudXExtraParameters.mm +117 -0
  18. package/lib/commonjs/CloudXAdViewBase.js +248 -0
  19. package/lib/commonjs/CloudXAdViewBase.js.map +1 -0
  20. package/lib/commonjs/CloudXBannerView.js +27 -0
  21. package/lib/commonjs/CloudXBannerView.js.map +1 -0
  22. package/lib/commonjs/CloudXMRECView.js +27 -0
  23. package/lib/commonjs/CloudXMRECView.js.map +1 -0
  24. package/lib/commonjs/index.js +16 -0
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js +30 -0
  27. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js.map +1 -0
  28. package/lib/commonjs/version.js +5 -2
  29. package/lib/commonjs/version.js.map +1 -1
  30. package/lib/module/CloudXAdViewBase.js +240 -0
  31. package/lib/module/CloudXAdViewBase.js.map +1 -0
  32. package/lib/module/CloudXBannerView.js +19 -0
  33. package/lib/module/CloudXBannerView.js.map +1 -0
  34. package/lib/module/CloudXMRECView.js +19 -0
  35. package/lib/module/CloudXMRECView.js.map +1 -0
  36. package/lib/module/index.js +3 -0
  37. package/lib/module/index.js.map +1 -1
  38. package/lib/module/specs/CloudXAdViewNativeComponent.js +27 -0
  39. package/lib/module/specs/CloudXAdViewNativeComponent.js.map +1 -0
  40. package/lib/module/version.js +5 -2
  41. package/lib/module/version.js.map +1 -1
  42. package/lib/typescript/src/CloudXAdViewBase.d.ts +28 -0
  43. package/lib/typescript/src/CloudXAdViewBase.d.ts.map +1 -0
  44. package/lib/typescript/src/CloudXBannerView.d.ts +16 -0
  45. package/lib/typescript/src/CloudXBannerView.d.ts.map +1 -0
  46. package/lib/typescript/src/CloudXMRECView.d.ts +16 -0
  47. package/lib/typescript/src/CloudXMRECView.d.ts.map +1 -0
  48. package/lib/typescript/src/index.d.ts +3 -0
  49. package/lib/typescript/src/index.d.ts.map +1 -1
  50. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts +62 -0
  51. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts.map +1 -0
  52. package/lib/typescript/src/types/AdProps.d.ts +3 -0
  53. package/lib/typescript/src/types/AdProps.d.ts.map +1 -1
  54. package/lib/typescript/src/version.d.ts +1 -1
  55. package/lib/typescript/src/version.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/src/CloudXAdViewBase.tsx +293 -0
  58. package/src/CloudXBannerView.tsx +20 -0
  59. package/src/CloudXMRECView.tsx +20 -0
  60. package/src/index.ts +5 -0
  61. package/src/specs/CloudXAdViewNativeComponent.ts +84 -0
  62. package/src/types/AdProps.ts +4 -0
  63. package/src/version.ts +5 -2
package/ios/CloudX.mm CHANGED
@@ -4,6 +4,7 @@
4
4
  //
5
5
 
6
6
  #import "CloudX.h"
7
+ #import "CloudXExtraParameters.h"
7
8
  #import <React/RCTUtils.h>
8
9
 
9
10
 
@@ -431,7 +432,9 @@ RCT_EXPORT_METHOD(loadInterstitial:(NSString *)adUnitIdentifier)
431
432
  RCT_EXPORT_METHOD(setInterstitialExtraParameters:(NSString *)adUnitIdentifier extraParameters:(NSDictionary<NSString *, id> *)extraParameters)
432
433
  {
433
434
  CLXInterstitial *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
434
- [self applyExtraParameters: extraParameters toFullscreenAd: interstitial];
435
+ [CloudXExtraParameters applyExtraParameters: extraParameters toFullscreenAd: interstitial logBlock:^(NSString *message) {
436
+ [self log: @"%@", message];
437
+ }];
435
438
  }
436
439
 
437
440
  RCT_EXPORT_METHOD(isInterstitialReady:(NSString *)adUnitIdentifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@@ -483,7 +486,9 @@ RCT_EXPORT_METHOD(loadRewarded:(NSString *)adUnitIdentifier)
483
486
  RCT_EXPORT_METHOD(setRewardedExtraParameters:(NSString *)adUnitIdentifier extraParameters:(NSDictionary<NSString *, id> *)extraParameters)
484
487
  {
485
488
  CLXRewarded *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
486
- [self applyExtraParameters: extraParameters toFullscreenAd: rewardedAd];
489
+ [CloudXExtraParameters applyExtraParameters: extraParameters toFullscreenAd: rewardedAd logBlock:^(NSString *message) {
490
+ [self log: @"%@", message];
491
+ }];
487
492
  }
488
493
 
489
494
  RCT_EXPORT_METHOD(isRewardedReady:(NSString *)adUnitIdentifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@@ -876,7 +881,9 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
876
881
  CLXBannerAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
877
882
  if ( adView )
878
883
  {
879
- [self applyExtraParameters: extraParameters toAdView: adView];
884
+ [CloudXExtraParameters applyExtraParameters: extraParameters toAdView: adView logBlock:^(NSString *message) {
885
+ [self log: @"%@", message];
886
+ }];
880
887
  return;
881
888
  }
882
889
 
@@ -887,17 +894,7 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
887
894
  self.adViewExtraParameters[adUnitIdentifier] = pending;
888
895
  }
889
896
 
890
- [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
891
- if ( !key || key.length == 0 ) return;
892
- if ( !value || [value isKindOfClass: [NSNull class]] )
893
- {
894
- [pending removeObjectForKey: key];
895
- }
896
- else
897
- {
898
- pending[key] = value;
899
- }
900
- }];
897
+ [CloudXExtraParameters mergeExtraParameters: extraParameters intoPendingDictionary: pending];
901
898
  }];
902
899
  }
903
900
 
@@ -905,49 +902,8 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
905
902
  {
906
903
  NSDictionary<NSString *, id> *pending = [self.adViewExtraParameters[adUnitIdentifier] copy];
907
904
  if ( pending.count == 0 ) return;
908
- [self applyExtraParameters: pending toAdView: adView];
909
- }
910
-
911
- - (void)applyExtraParameterValue:(id)value forKey:(NSString *)key toTarget:(id)target
912
- {
913
- if ( !target || !key || key.length == 0 ) return;
914
-
915
- SEL selector = NSSelectorFromString(@"setExtraParameter:value:");
916
- if ( ![target respondsToSelector: selector] )
917
- {
918
- selector = NSSelectorFromString(@"setExtraParameterForKey:value:");
919
- }
920
-
921
- if ( ![target respondsToSelector: selector] )
922
- {
923
- [self log: @"Skipping extra parameter \"%@\" because %@ does not respond to setExtraParameter:value: or setExtraParameterForKey:value:", key, NSStringFromClass([target class])];
924
- return;
925
- }
926
-
927
- using SetExtraParameterMethod = void (*)(id, SEL, NSString *, id);
928
- SetExtraParameterMethod method = (SetExtraParameterMethod)[target methodForSelector: selector];
929
- method(target, selector, key, value);
930
-
931
- [self log: @"Applied extra parameter \"%@\" with value \"%@\" to %@", key, value ?: @"(cleared)", NSStringFromClass([target class])];
932
- }
933
-
934
- - (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters toAdView:(CLXBannerAdView *)adView
935
- {
936
- [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
937
- if ( !key || key.length == 0 ) return;
938
- id normalizedValue = [value isKindOfClass: [NSNull class]] ? nil : value;
939
- SEL selector = NSSelectorFromString(@"setExtraParameter:value:");
940
- id target = [adView respondsToSelector: selector] ? adView : adView.ad;
941
- [self applyExtraParameterValue: normalizedValue forKey: key toTarget: target];
942
- }];
943
- }
944
-
945
- - (void)applyExtraParameters:(NSDictionary<NSString *, id> *)extraParameters toFullscreenAd:(id<CLXFullscreenAd>)ad
946
- {
947
- [extraParameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
948
- if ( !key || key.length == 0 ) return;
949
- id normalizedValue = [value isKindOfClass: [NSNull class]] ? nil : value;
950
- [self applyExtraParameterValue: normalizedValue forKey: key toTarget: ad];
905
+ [CloudXExtraParameters applyExtraParameters: pending toAdView: adView logBlock:^(NSString *message) {
906
+ [self log: @"%@", message];
951
907
  }];
952
908
  }
953
909
 
@@ -0,0 +1,35 @@
1
+ /*
2
+ * CloudXAdView.h
3
+ * cloudx-react-native
4
+ *
5
+ * In-tree banner/MREC view component. Unlike the imperative overlay banner in
6
+ * CloudX.mm (attached above the whole RN view tree), this hosts a
7
+ * CLXBannerAdView as an ordinary RN view so app overlays layer above it.
8
+ */
9
+
10
+ #import <React/RCTUIManager.h>
11
+ #import <CloudXCore/CloudXCore.h>
12
+
13
+ #ifdef RCT_NEW_ARCH_ENABLED
14
+ #import <React/RCTViewComponentView.h>
15
+ @interface CloudXAdView : RCTViewComponentView <CLXBannerDelegate, CLXAdRevenueDelegate>
16
+ #else
17
+ @interface CloudXAdView : UIView <CLXBannerDelegate, CLXAdRevenueDelegate>
18
+ #endif
19
+
20
+ NS_ASSUME_NONNULL_BEGIN
21
+
22
+ /*
23
+ * Nil by default on the Old Architecture (only set when the publisher passes
24
+ * the corresponding callback); delegate call sites guard with `if (self.onX)`.
25
+ */
26
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdLoadedEvent;
27
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdLoadFailedEvent;
28
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdClickedEvent;
29
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdRevenuePaidEvent;
30
+
31
+ - (void)setExtraParametersFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)extraParameters;
32
+
33
+ @end
34
+
35
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,505 @@
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
+ shouldAttach = YES;
178
+ }
179
+ if ( oldViewProps.placement != newViewProps.placement )
180
+ {
181
+ [self setPlacement: RCTNSStringFromStringNilIfEmpty(newViewProps.placement)];
182
+ }
183
+ if ( oldViewProps.customData != newViewProps.customData )
184
+ {
185
+ [self setCustomData: RCTNSStringFromStringNilIfEmpty(newViewProps.customData)];
186
+ }
187
+ /*
188
+ * CloudXAdViewExtraParametersStruct contains folly::dynamic, so the
189
+ * generated props do not support a simple old/new equality guard like the
190
+ * scalar props above. Applying is idempotent, with removed keys cleared by
191
+ * applyExtraParametersIfReady.
192
+ */
193
+ [self setExtraParametersFromEntryArray: [self entryArrayFromFabricExtraParameters: newViewProps.extraParameters]];
194
+
195
+ if ( shouldAttach )
196
+ {
197
+ [self attachAdViewIfNeeded];
198
+ }
199
+
200
+ [super updateProps: props oldProps: oldProps];
201
+ }
202
+
203
+ - (void)prepareForRecycle
204
+ {
205
+ [super prepareForRecycle];
206
+
207
+ static const auto defaultProps = std::make_shared<const CloudXAdViewProps>();
208
+ _props = defaultProps;
209
+
210
+ [self destroyAdIfNeeded];
211
+ self.adUnitId = nil;
212
+ self.adFormat = nil;
213
+ self.placement = nil;
214
+ self.customData = nil;
215
+ self.extraParameters = @{};
216
+ self.appliedExtraParameterKeys = [NSSet set];
217
+ }
218
+
219
+ - (NSArray<NSDictionary<NSString *, id> *> *)entryArrayFromFabricExtraParameters:(const std::vector<CloudXAdViewExtraParametersStruct> &)entries
220
+ {
221
+ NSMutableArray<NSDictionary<NSString *, id> *> *result = [NSMutableArray arrayWithCapacity: entries.size()];
222
+ for ( const auto &entry : entries )
223
+ {
224
+ NSString *key = [NSString stringWithUTF8String: entry.key.c_str()] ?: @"";
225
+ id value = CLXIdFromFollyDynamic(entry.value) ?: [NSNull null];
226
+ [result addObject: @{@"key": key, @"value": value}];
227
+ }
228
+ return [result copy];
229
+ }
230
+
231
+ #else
232
+
233
+ - (instancetype)init
234
+ {
235
+ self = [super init];
236
+ if ( self )
237
+ {
238
+ [self commonInit];
239
+ }
240
+ return self;
241
+ }
242
+
243
+ #endif // RCT_NEW_ARCH_ENABLED
244
+
245
+ #pragma mark - Property setters (shared; Paper view manager sets these via KVC)
246
+
247
+ - (void)setAdUnitId:(NSString *)adUnitId
248
+ {
249
+ if ( self.adView )
250
+ {
251
+ /*
252
+ * Ad unit ID is immutable once the ad view is created. Warn only on an
253
+ * actual change; unchanged re-sets on re-render stay quiet.
254
+ */
255
+ if ( ![adUnitId isEqualToString: _adUnitId] )
256
+ {
257
+ RCTLogWarn(@"CloudXAdView: adUnitId cannot change after the ad view is created; remount the component (e.g. via a React key).");
258
+ }
259
+ return;
260
+ }
261
+ _adUnitId = adUnitId;
262
+ }
263
+
264
+ - (void)setAdFormat:(NSString *)adFormat
265
+ {
266
+ if ( self.adView )
267
+ {
268
+ if ( ![adFormat isEqualToString: _adFormat] )
269
+ {
270
+ RCTLogWarn(@"CloudXAdView: adFormat cannot change after the ad view is created; remount the component (e.g. via a React key).");
271
+ }
272
+ return;
273
+ }
274
+ _adFormat = adFormat;
275
+ }
276
+
277
+ - (void)setPlacement:(NSString *)placement
278
+ {
279
+ _placement = placement;
280
+ self.adView.placement = placement;
281
+ }
282
+
283
+ - (void)setCustomData:(NSString *)customData
284
+ {
285
+ _customData = customData;
286
+ self.adView.customData = customData;
287
+ }
288
+
289
+ - (void)setExtraParametersFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)extraParameters
290
+ {
291
+ self.extraParameters = [CloudXExtraParameters dictionaryFromEntryArray: extraParameters];
292
+ [self applyExtraParametersIfReady];
293
+ }
294
+
295
+ #pragma mark - Paper attach hook
296
+
297
+ // Invoked after all JS props are set when mounting on the Old Architecture.
298
+ - (void)didSetProps:(NSArray<NSString *> *)changedProps
299
+ {
300
+ [self attachAdViewIfNeeded];
301
+ }
302
+
303
+ #pragma mark - Ad view lifecycle
304
+
305
+ - (void)attachAdViewIfNeeded
306
+ {
307
+ NSString *adUnitId = self.adUnitId;
308
+ NSString *adFormat = self.adFormat;
309
+
310
+ dispatch_async(dispatch_get_main_queue(), ^{
311
+ if ( self.adView ) return;
312
+ if ( adUnitId.length == 0 || adFormat.length == 0 ) return;
313
+
314
+ CLXBannerAdView *adView;
315
+ if ( [adFormat caseInsensitiveCompare: @"MREC"] == NSOrderedSame )
316
+ {
317
+ adView = [[CloudXCore shared] createMRECWithAdUnitId: adUnitId];
318
+ }
319
+ else
320
+ {
321
+ adView = [[CloudXCore shared] createBannerWithAdUnitId: adUnitId];
322
+ }
323
+
324
+ if ( !adView )
325
+ {
326
+ /*
327
+ * createBanner/createMRECWithAdUnitId: returned nil. The JS layer
328
+ * gates mounting on SDK initialization, so a nil here post-init is
329
+ * an invalid/expired ad unit - surface it as a load failure instead
330
+ * of leaving a permanently-dead, silent view (self.adView stays nil
331
+ * and props don't re-land, so there is no retry).
332
+ */
333
+ if ( self.onAdLoadFailedEvent )
334
+ {
335
+ self.onAdLoadFailedEvent(@{@"adUnitId": adUnitId ?: @"",
336
+ @"code": @(CLXErrorCodeInvalidAdUnit),
337
+ @"message": @"Ad view creation returned nil (invalid ad unit or SDK not initialized)"});
338
+ }
339
+ return;
340
+ }
341
+
342
+ adView.delegate = self;
343
+ adView.revenueDelegate = self;
344
+ adView.placement = self.placement;
345
+ adView.customData = self.customData;
346
+ adView.userInteractionEnabled = YES;
347
+ adView.translatesAutoresizingMaskIntoConstraints = NO;
348
+
349
+ self.adView = adView;
350
+ [self applyExtraParametersIfReady];
351
+ [self addSubview: adView];
352
+ [NSLayoutConstraint activateConstraints: @[
353
+ [adView.topAnchor constraintEqualToAnchor: self.topAnchor],
354
+ [adView.bottomAnchor constraintEqualToAnchor: self.bottomAnchor],
355
+ [adView.leadingAnchor constraintEqualToAnchor: self.leadingAnchor],
356
+ [adView.trailingAnchor constraintEqualToAnchor: self.trailingAnchor],
357
+ ]];
358
+
359
+ [adView load];
360
+ [self updateAutoRefreshForWindowVisibility];
361
+ });
362
+ }
363
+
364
+ - (void)updateAutoRefreshForWindowVisibility
365
+ {
366
+ if ( !self.adView ) return;
367
+ if ( self.window )
368
+ {
369
+ [self.adView startAutoRefresh];
370
+ }
371
+ else
372
+ {
373
+ [self.adView stopAutoRefresh];
374
+ }
375
+ }
376
+
377
+ - (void)didMoveToWindow
378
+ {
379
+ [super didMoveToWindow];
380
+ [self updateAutoRefreshForWindowVisibility];
381
+ }
382
+
383
+ - (void)didMoveToSuperview
384
+ {
385
+ [super didMoveToSuperview];
386
+
387
+ if ( self.superview != nil )
388
+ {
389
+ [self updateAutoRefreshForWindowVisibility];
390
+ return;
391
+ }
392
+
393
+ /*
394
+ * Superview nil could be a real unmount or brief reparenting (react-native-screens).
395
+ * Defer one main-queue turn; destroy only if it is still detached. Strong ref so the
396
+ * ad is always torn down on a genuine unmount.
397
+ */
398
+ CloudXAdView *viewRef = self;
399
+ dispatch_async(dispatch_get_main_queue(), ^{
400
+ if ( viewRef.superview != nil )
401
+ {
402
+ [viewRef updateAutoRefreshForWindowVisibility];
403
+ return;
404
+ }
405
+ [viewRef destroyAdIfNeeded];
406
+ });
407
+ }
408
+
409
+ - (void)dealloc
410
+ {
411
+ [self destroyAdIfNeeded];
412
+ }
413
+
414
+ - (void)destroyAdIfNeeded
415
+ {
416
+ if ( !self.adView ) return;
417
+
418
+ [self.adView stopAutoRefresh];
419
+ self.adView.delegate = nil;
420
+ self.adView.revenueDelegate = nil;
421
+ [self.adView destroy];
422
+ [self.adView removeFromSuperview];
423
+ self.adView = nil;
424
+ self.appliedExtraParameterKeys = [NSSet set];
425
+ }
426
+
427
+ - (void)applyExtraParametersIfReady
428
+ {
429
+ if ( !self.adView ) return;
430
+
431
+ NSDictionary<NSString *, id> *extraParameters = self.extraParameters ?: @{};
432
+ [CloudXExtraParameters applyExtraParameters: extraParameters toAdView: self.adView logBlock: nil];
433
+
434
+ NSMutableDictionary<NSString *, id> *removed = [NSMutableDictionary dictionary];
435
+ for ( NSString *key in self.appliedExtraParameterKeys )
436
+ {
437
+ if ( !extraParameters[key] )
438
+ {
439
+ removed[key] = [NSNull null];
440
+ }
441
+ }
442
+ [CloudXExtraParameters applyExtraParameters: removed toAdView: self.adView logBlock: nil];
443
+ self.appliedExtraParameterKeys = [NSSet setWithArray: extraParameters.allKeys];
444
+ }
445
+
446
+ #pragma mark - Event payloads
447
+
448
+ - (NSDictionary<NSString *, id> *)adInfoDictForAd:(CLXAd *)ad
449
+ {
450
+ NSString *adValuesJson = @"";
451
+ if ( ad.adValues )
452
+ {
453
+ NSData *data = [NSJSONSerialization dataWithJSONObject: ad.adValues options: 0 error: nil];
454
+ if ( data )
455
+ {
456
+ adValuesJson = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] ?: @"";
457
+ }
458
+ }
459
+
460
+ return @{@"adUnitId": ad.adUnitId ?: @"",
461
+ @"adFormat": self.adFormat ?: @"",
462
+ @"networkName": ad.networkName ?: @"",
463
+ @"networkPlacement": ad.networkPlacement ?: @"",
464
+ @"placement": ad.placement ?: @"",
465
+ @"revenue": ad.revenue ?: @(0),
466
+ @"adValuesJson": adValuesJson};
467
+ }
468
+
469
+ - (NSDictionary<NSString *, id> *)loadFailedDictForAdUnit:(NSString *)adUnitId error:(CLXError *)error
470
+ {
471
+ return @{@"adUnitId": adUnitId ?: (self.adUnitId ?: @""),
472
+ @"code": error ? @(error.code) : @(CLXErrorCodeInternalError),
473
+ @"message": error.localizedDescription ?: @""};
474
+ }
475
+
476
+ #pragma mark - CLXBannerDelegate / CLXAdRevenueDelegate
477
+
478
+ - (void)didLoadAd:(CLXAd *)ad
479
+ {
480
+ if ( self.onAdLoadedEvent ) self.onAdLoadedEvent([self adInfoDictForAd: ad]);
481
+ }
482
+
483
+ - (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error
484
+ {
485
+ if ( self.onAdLoadFailedEvent ) self.onAdLoadFailedEvent([self loadFailedDictForAdUnit: adUnitId error: error]);
486
+ }
487
+
488
+ - (void)didClickAd:(CLXAd *)ad
489
+ {
490
+ if ( self.onAdClickedEvent ) self.onAdClickedEvent([self adInfoDictForAd: ad]);
491
+ }
492
+
493
+ - (void)didPayRevenueForAd:(CLXAd *)ad
494
+ {
495
+ if ( self.onAdRevenuePaidEvent ) self.onAdRevenuePaidEvent([self adInfoDictForAd: ad]);
496
+ }
497
+
498
+ @end
499
+
500
+ #ifdef RCT_NEW_ARCH_ENABLED
501
+ Class<RCTComponentViewProtocol> CloudXAdViewCls(void)
502
+ {
503
+ return [CloudXAdView class];
504
+ }
505
+ #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