cloudx-react-native 3.11.0 → 3.11.1
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.
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +9 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXAdInfo.kt +76 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +110 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXNativeAdHolder.kt +184 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXNativeAdView.kt +209 -70
- package/android/src/main/java/com/cloudxreactnative/CloudXNativeAdViewManagerImpl.kt +6 -0
- package/android/src/newarch/com/cloudxreactnative/CloudXModule.kt +9 -0
- package/android/src/newarch/com/cloudxreactnative/CloudXNativeAdViewManager.kt +2 -0
- package/android/src/oldarch/com/cloudxreactnative/CloudXModule.kt +14 -0
- package/android/src/oldarch/com/cloudxreactnative/CloudXNativeAdViewManager.kt +3 -0
- package/android/src/test/java/com/cloudxreactnative/CloudXNativeAdPreloadTest.kt +351 -0
- package/android/src/test/java/com/cloudxreactnative/FakeReadable.kt +2 -2
- package/ios/CloudX.h +12 -0
- package/ios/CloudX.mm +94 -1
- package/ios/CloudXNativeAdHolder.h +66 -0
- package/ios/CloudXNativeAdHolder.mm +247 -0
- package/ios/CloudXNativeAdView.h +9 -0
- package/ios/CloudXNativeAdView.mm +308 -64
- package/ios/CloudXNativeAdViewManager.mm +7 -0
- package/lib/commonjs/CloudXNativeAd.js +178 -0
- package/lib/commonjs/CloudXNativeAd.js.map +1 -0
- package/lib/commonjs/CloudXNativeAdView.js +58 -11
- package/lib/commonjs/CloudXNativeAdView.js.map +1 -1
- package/lib/commonjs/hooks.js +203 -0
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/CloudXNativeAdViewNativeComponent.js +2 -0
- package/lib/commonjs/specs/CloudXNativeAdViewNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/NativeCloudX.js.map +1 -1
- package/lib/commonjs/types/NativeAd.js +21 -1
- package/lib/commonjs/types/NativeAd.js.map +1 -1
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/CloudXNativeAd.js +170 -0
- package/lib/module/CloudXNativeAd.js.map +1 -0
- package/lib/module/CloudXNativeAdView.js +58 -11
- package/lib/module/CloudXNativeAdView.js.map +1 -1
- package/lib/module/hooks.js +202 -0
- package/lib/module/hooks.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/CloudXNativeAdViewNativeComponent.js +2 -0
- package/lib/module/specs/CloudXNativeAdViewNativeComponent.js.map +1 -1
- package/lib/module/specs/NativeCloudX.js.map +1 -1
- package/lib/module/types/NativeAd.js +26 -1
- package/lib/module/types/NativeAd.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/CloudXNativeAd.d.ts +58 -0
- package/lib/typescript/src/CloudXNativeAd.d.ts.map +1 -0
- package/lib/typescript/src/CloudXNativeAdView.d.ts +6 -13
- package/lib/typescript/src/CloudXNativeAdView.d.ts.map +1 -1
- package/lib/typescript/src/hooks.d.ts +30 -0
- package/lib/typescript/src/hooks.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/CloudXNativeAdViewNativeComponent.d.ts +23 -2
- package/lib/typescript/src/specs/CloudXNativeAdViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeCloudX.d.ts +13 -0
- package/lib/typescript/src/specs/NativeCloudX.d.ts.map +1 -1
- package/lib/typescript/src/types/NativeAd.d.ts +76 -13
- package/lib/typescript/src/types/NativeAd.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts +1 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/CloudXNativeAd.ts +177 -0
- package/src/CloudXNativeAdView.tsx +82 -15
- package/src/hooks.ts +236 -0
- package/src/index.ts +14 -1
- package/src/specs/CloudXNativeAdViewNativeComponent.ts +27 -2
- package/src/specs/NativeCloudX.ts +20 -0
- package/src/types/NativeAd.ts +85 -18
- package/src/types/index.ts +4 -0
- package/src/version.ts +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#import "CloudXNativeAdView.h"
|
|
7
7
|
#import "CloudX.h"
|
|
8
|
+
#import "CloudXNativeAdHolder.h"
|
|
8
9
|
#import <React/RCTLog.h>
|
|
9
10
|
// Supplies `UIView.reactTag`, which Paper uses instead of `UIView.tag`.
|
|
10
11
|
#import <React/UIView+React.h>
|
|
@@ -144,6 +145,12 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
144
145
|
@property (nonatomic, strong) NSMutableArray<UIView *> *insertedSDKSubviews;
|
|
145
146
|
|
|
146
147
|
@property (nonatomic, assign) BOOL hasRequestedLoad;
|
|
148
|
+
@property (nonatomic, assign) BOOL ownsLoader;
|
|
149
|
+
@property (nonatomic, weak, nullable) CloudXNativeAdHolder *boundHolder;
|
|
150
|
+
// Set once a preloaded ad has been presented here (template rendered, or the
|
|
151
|
+
// manual bind handed to JS), so a window re-attach (tab switch, screen
|
|
152
|
+
// transition) keeps the existing presentation instead of rendering again.
|
|
153
|
+
@property (nonatomic, assign) BOOL preloadedAdPresented;
|
|
147
154
|
|
|
148
155
|
@end
|
|
149
156
|
|
|
@@ -196,6 +203,25 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
196
203
|
emitter->onAdLoadedEvent(result);
|
|
197
204
|
};
|
|
198
205
|
|
|
206
|
+
self.onAdDisplayedEvent = ^(NSDictionary *event) {
|
|
207
|
+
CloudXNativeAdView *strongSelf = weakSelf;
|
|
208
|
+
if ( !strongSelf || !strongSelf->_eventEmitter ) return;
|
|
209
|
+
auto emitter = std::static_pointer_cast<CloudXNativeAdViewEventEmitter const>(strongSelf->_eventEmitter);
|
|
210
|
+
CloudXNativeAdViewEventEmitter::OnAdDisplayedEvent result;
|
|
211
|
+
CLX_FILL_NATIVE_ADINFO(result, event);
|
|
212
|
+
emitter->onAdDisplayedEvent(result);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
self.onAdBindingClearedEvent = ^(NSDictionary *event) {
|
|
216
|
+
CloudXNativeAdView *strongSelf = weakSelf;
|
|
217
|
+
if ( !strongSelf || !strongSelf->_eventEmitter ) return;
|
|
218
|
+
auto emitter = std::static_pointer_cast<CloudXNativeAdViewEventEmitter const>(strongSelf->_eventEmitter);
|
|
219
|
+
CloudXNativeAdViewEventEmitter::OnAdBindingClearedEvent result;
|
|
220
|
+
NSString *clearedId = [event[@"nativeAdId"] isKindOfClass: [NSString class]] ? event[@"nativeAdId"] : @"";
|
|
221
|
+
result.nativeAdId = std::string([clearedId UTF8String] ?: "");
|
|
222
|
+
emitter->onAdBindingClearedEvent(result);
|
|
223
|
+
};
|
|
224
|
+
|
|
199
225
|
self.onAdClickedEvent = ^(NSDictionary *event) {
|
|
200
226
|
CloudXNativeAdView *strongSelf = weakSelf;
|
|
201
227
|
if ( !strongSelf || !strongSelf->_eventEmitter ) return;
|
|
@@ -264,6 +290,10 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
264
290
|
{
|
|
265
291
|
self.adUnitId = RCTNSStringFromString(newViewProps.adUnitId);
|
|
266
292
|
}
|
|
293
|
+
if ( oldViewProps.nativeAdId != newViewProps.nativeAdId )
|
|
294
|
+
{
|
|
295
|
+
self.nativeAdId = RCTNSStringFromStringNilIfEmpty(newViewProps.nativeAdId);
|
|
296
|
+
}
|
|
267
297
|
if ( oldViewProps.templateName != newViewProps.templateName )
|
|
268
298
|
{
|
|
269
299
|
self.templateName = RCTNSStringFromStringNilIfEmpty(newViewProps.templateName);
|
|
@@ -323,9 +353,18 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
323
353
|
// persistent, stateful per-instance object. A recycled Fabric view must
|
|
324
354
|
// never leak the prior mount's ad/loader state into the next mount.
|
|
325
355
|
// destroyAd detaches the delegates, destroys the loader and nils it.
|
|
326
|
-
|
|
356
|
+
if ( self.nativeAdId.length > 0 )
|
|
357
|
+
{
|
|
358
|
+
[self teardownPreloadedBinding];
|
|
359
|
+
}
|
|
360
|
+
else
|
|
361
|
+
{
|
|
362
|
+
[self destroyAd];
|
|
363
|
+
}
|
|
327
364
|
|
|
365
|
+
self.ownsLoader = NO;
|
|
328
366
|
self.adUnitId = nil;
|
|
367
|
+
self.nativeAdId = nil;
|
|
329
368
|
self.templateName = nil;
|
|
330
369
|
self.placement = nil;
|
|
331
370
|
self.customData = nil;
|
|
@@ -354,11 +393,41 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
354
393
|
|
|
355
394
|
#pragma mark - Property setters (shared; Paper view manager sets these via KVC)
|
|
356
395
|
|
|
396
|
+
- (void)setNativeAdId:(nullable NSString *)nativeAdId
|
|
397
|
+
{
|
|
398
|
+
NSString *normalized = (nativeAdId.length > 0) ? [nativeAdId copy] : nil;
|
|
399
|
+
if ( [normalized isEqual: _nativeAdId] ) return;
|
|
400
|
+
|
|
401
|
+
[self teardownPreloadedBinding];
|
|
402
|
+
|
|
403
|
+
_nativeAdId = normalized;
|
|
404
|
+
if ( _nativeAdId.length == 0 ) return;
|
|
405
|
+
|
|
406
|
+
// Resolve holder immediately so manual-mode commands find pendingManualAd even before attach
|
|
407
|
+
CloudXNativeAdHolder *holder = [CloudXNativeAdHolder holderForId: _nativeAdId];
|
|
408
|
+
if ( holder )
|
|
409
|
+
{
|
|
410
|
+
self.pendingManualAd = holder.ad;
|
|
411
|
+
self.boundHolder = holder;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if ( self.window )
|
|
415
|
+
{
|
|
416
|
+
[self bindPreloadedAdIfNeeded];
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
357
420
|
- (void)setAdUnitId:(NSString *)adUnitId
|
|
358
421
|
{
|
|
359
422
|
NSString *normalized = [adUnitId copy];
|
|
360
423
|
if ( [normalized isEqual: _adUnitId] ) return;
|
|
361
424
|
|
|
425
|
+
if ( self.nativeAdId.length > 0 )
|
|
426
|
+
{
|
|
427
|
+
_adUnitId = normalized;
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
362
431
|
// First-time set (no loader yet): just store the value. didMoveToWindow /
|
|
363
432
|
// loadIfNeeded loads once the view is attached.
|
|
364
433
|
if ( !self.nativeAdLoader )
|
|
@@ -411,7 +480,17 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
411
480
|
// hierarchy, so manualRender is settled by the time this callback fires.
|
|
412
481
|
// Auto-load runs in both render modes; the mode only controls how
|
|
413
482
|
// didLoadNativeAd:forAd: presents the result.
|
|
414
|
-
if ( self.window )
|
|
483
|
+
if ( self.window )
|
|
484
|
+
{
|
|
485
|
+
if ( self.nativeAdId.length > 0 )
|
|
486
|
+
{
|
|
487
|
+
[self bindPreloadedAdIfNeeded];
|
|
488
|
+
}
|
|
489
|
+
else
|
|
490
|
+
{
|
|
491
|
+
[self loadIfNeeded];
|
|
492
|
+
}
|
|
493
|
+
}
|
|
415
494
|
}
|
|
416
495
|
|
|
417
496
|
- (CLXNativeAdLoader *)resolveLoader
|
|
@@ -425,6 +504,7 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
425
504
|
self.nativeAdLoader = [[CloudXCore shared] createNativeAdLoaderWithAdUnitIdentifier: self.adUnitId];
|
|
426
505
|
self.nativeAdLoader.nativeAdDelegate = self;
|
|
427
506
|
self.nativeAdLoader.revenueDelegate = self;
|
|
507
|
+
self.ownsLoader = YES;
|
|
428
508
|
}
|
|
429
509
|
|
|
430
510
|
return self.nativeAdLoader;
|
|
@@ -432,50 +512,25 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
432
512
|
|
|
433
513
|
- (void)loadIfNeeded
|
|
434
514
|
{
|
|
515
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
435
516
|
if ( self.hasRequestedLoad || self.adUnitId.length == 0 ) return;
|
|
436
517
|
|
|
437
518
|
CLXNativeAdLoader *loader = [self resolveLoader];
|
|
438
519
|
if ( !loader ) return;
|
|
439
520
|
|
|
440
|
-
loader
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
521
|
+
[CloudXNativeAdHolder configureLoader: loader
|
|
522
|
+
placement: self.placement
|
|
523
|
+
customData: self.customData
|
|
524
|
+
disableVideoFullScreen: self.disableVideoFullScreen
|
|
525
|
+
startVideoUnmuted: self.startVideoUnmuted
|
|
526
|
+
hideVideoMediaControls: self.hideVideoMediaControls
|
|
527
|
+
extraParameterEntries: self.extraParameterEntries
|
|
528
|
+
localExtraParameterEntries: self.localExtraParameterEntries];
|
|
447
529
|
|
|
448
530
|
self.hasRequestedLoad = YES;
|
|
449
531
|
[loader loadAd];
|
|
450
532
|
}
|
|
451
533
|
|
|
452
|
-
- (void)applyExtraParametersToLoader:(CLXNativeAdLoader *)loader
|
|
453
|
-
{
|
|
454
|
-
// JS null crosses the bridge as NSNull; normalize to nil so the loader
|
|
455
|
-
// never receives NSNull.
|
|
456
|
-
//
|
|
457
|
-
// extraParameters are bid-level auction signals (setExtraParameter:value:);
|
|
458
|
-
// localExtraParameters are adapter-only and never sent in the bid request
|
|
459
|
-
// (setLocalExtraParameterForKey:value:) — see CLXNativeAdLoader.h.
|
|
460
|
-
for ( NSDictionary *entry in self.extraParameterEntries )
|
|
461
|
-
{
|
|
462
|
-
NSString *key = [entry[@"key"] isKindOfClass: [NSString class]] ? entry[@"key"] : nil;
|
|
463
|
-
if ( key.length == 0 ) continue;
|
|
464
|
-
id value = entry[@"value"];
|
|
465
|
-
if ( value == [NSNull null] ) value = nil;
|
|
466
|
-
[loader setExtraParameter: key value: value];
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
for ( NSDictionary *entry in self.localExtraParameterEntries )
|
|
470
|
-
{
|
|
471
|
-
NSString *key = [entry[@"key"] isKindOfClass: [NSString class]] ? entry[@"key"] : nil;
|
|
472
|
-
if ( key.length == 0 ) continue;
|
|
473
|
-
id value = entry[@"value"];
|
|
474
|
-
if ( value == [NSNull null] ) value = nil;
|
|
475
|
-
[loader setLocalExtraParameterForKey: key value: value];
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
534
|
#pragma mark - Commands
|
|
480
535
|
//
|
|
481
536
|
// Method names match RCTCloudXNativeAdViewViewProtocol exactly, so Fabric's
|
|
@@ -485,6 +540,7 @@ static NSArray<NSDictionary<NSString *, id> *> *CLXEntryArrayFromNativeAdViewStr
|
|
|
485
540
|
|
|
486
541
|
- (void)loadAd
|
|
487
542
|
{
|
|
543
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
488
544
|
// Explicit JS-driven (re)load bypasses the "already requested" guard used
|
|
489
545
|
// by the didMoveToWindow auto-load.
|
|
490
546
|
self.hasRequestedLoad = NO;
|
|
@@ -636,6 +692,14 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
636
692
|
if ( registered )
|
|
637
693
|
{
|
|
638
694
|
[self clx_updateContainerClickRecognizerForAd: ad];
|
|
695
|
+
if ( self.boundHolder )
|
|
696
|
+
{
|
|
697
|
+
self.boundHolder.boundView = self;
|
|
698
|
+
}
|
|
699
|
+
if ( self.onAdDisplayedEvent )
|
|
700
|
+
{
|
|
701
|
+
self.onAdDisplayedEvent([self adInfoDictForAd: ad]);
|
|
702
|
+
}
|
|
639
703
|
return;
|
|
640
704
|
}
|
|
641
705
|
|
|
@@ -696,6 +760,9 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
696
760
|
|
|
697
761
|
- (void)destroyAd
|
|
698
762
|
{
|
|
763
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
764
|
+
if ( !self.ownsLoader ) return;
|
|
765
|
+
|
|
699
766
|
// Invalidate first: a load already in flight belongs to the generation
|
|
700
767
|
// being torn down, and its callback must not render into this host.
|
|
701
768
|
self.loadGeneration++;
|
|
@@ -752,6 +819,16 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
752
819
|
|
|
753
820
|
- (void)dealloc
|
|
754
821
|
{
|
|
822
|
+
if ( !_ownsLoader )
|
|
823
|
+
{
|
|
824
|
+
CloudXNativeAdHolder *holder = _boundHolder;
|
|
825
|
+
if ( holder && holder.boundView == self )
|
|
826
|
+
{
|
|
827
|
+
holder.boundView = nil;
|
|
828
|
+
}
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
755
832
|
// The loader is owned by this host. Destroying it releases the SDK-side
|
|
756
833
|
// ad pipeline and cancels any in-flight auction so nothing calls back
|
|
757
834
|
// into the zombie delegate. Keep strong local references across the
|
|
@@ -773,6 +850,195 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
773
850
|
});
|
|
774
851
|
}
|
|
775
852
|
|
|
853
|
+
#pragma mark - Preloaded ad binding
|
|
854
|
+
|
|
855
|
+
- (BOOL)presentTemplateAd:(CLXAd *)ad
|
|
856
|
+
withNativeAdView:(nullable CLXNativeAdView *)nativeAdView
|
|
857
|
+
loader:(nullable CLXNativeAdLoader *)loader
|
|
858
|
+
{
|
|
859
|
+
CLXNativeAdView *view = nativeAdView;
|
|
860
|
+
BOOL rendered = YES;
|
|
861
|
+
if ( !view )
|
|
862
|
+
{
|
|
863
|
+
NSString *name = self.templateName.length > 0 ? self.templateName : @"medium_template";
|
|
864
|
+
view = [CLXNativeAdView viewFromAd: ad.nativeAd withTemplate: name];
|
|
865
|
+
rendered = loader ? [loader renderNativeAdView: view withAd: ad] : NO;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if ( !rendered )
|
|
869
|
+
{
|
|
870
|
+
[self.currentNativeView removeFromSuperview];
|
|
871
|
+
self.currentNativeView = nil;
|
|
872
|
+
self.currentTemplateAd = nil;
|
|
873
|
+
return NO;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
[self.currentNativeView removeFromSuperview];
|
|
877
|
+
self.currentNativeView = view;
|
|
878
|
+
self.currentTemplateAd = ad;
|
|
879
|
+
|
|
880
|
+
view.translatesAutoresizingMaskIntoConstraints = NO;
|
|
881
|
+
[self addSubview: view];
|
|
882
|
+
[NSLayoutConstraint activateConstraints: @[
|
|
883
|
+
[view.topAnchor constraintEqualToAnchor: self.topAnchor],
|
|
884
|
+
[view.bottomAnchor constraintEqualToAnchor: self.bottomAnchor],
|
|
885
|
+
[view.leadingAnchor constraintEqualToAnchor: self.leadingAnchor],
|
|
886
|
+
[view.trailingAnchor constraintEqualToAnchor: self.trailingAnchor],
|
|
887
|
+
]];
|
|
888
|
+
return YES;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
- (void)clearRenderedAdSilently
|
|
892
|
+
{
|
|
893
|
+
// A preloaded ad leaving this view also takes its React-rendered text and
|
|
894
|
+
// images with it: tell the JS view so its asset components clear.
|
|
895
|
+
BOOL hadPreloadedAd = self.nativeAdId.length > 0 &&
|
|
896
|
+
(self.preloadedAdPresented || self.pendingManualAd || self.currentTemplateAd);
|
|
897
|
+
if ( hadPreloadedAd && self.onAdBindingClearedEvent )
|
|
898
|
+
{
|
|
899
|
+
self.onAdBindingClearedEvent(@{ @"nativeAdId": self.nativeAdId });
|
|
900
|
+
}
|
|
901
|
+
self.preloadedAdPresented = NO;
|
|
902
|
+
self.pendingManualAd = nil;
|
|
903
|
+
self.currentTemplateAd = nil;
|
|
904
|
+
|
|
905
|
+
[self.currentNativeView removeFromSuperview];
|
|
906
|
+
self.currentNativeView = nil;
|
|
907
|
+
|
|
908
|
+
[self removeSDKInsertedSubviews];
|
|
909
|
+
[self.clickableViews removeAllObjects];
|
|
910
|
+
[self.assetViewsByRole removeAllObjects];
|
|
911
|
+
|
|
912
|
+
if ( self.containerClickRecognizer )
|
|
913
|
+
{
|
|
914
|
+
[self removeGestureRecognizer: self.containerClickRecognizer];
|
|
915
|
+
self.containerClickRecognizer = nil;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
- (void)handleBoundAdDestroyed
|
|
920
|
+
{
|
|
921
|
+
NSString *adUnitId = self.boundHolder.adUnitId ?: (self.adUnitId ?: @"");
|
|
922
|
+
[self clearRenderedAdSilently];
|
|
923
|
+
self.boundHolder = nil;
|
|
924
|
+
|
|
925
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdNotReady
|
|
926
|
+
description: @"native ad destroyed"];
|
|
927
|
+
if ( self.onAdDisplayFailedEvent )
|
|
928
|
+
{
|
|
929
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: adUnitId error: error]);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
- (void)handleBoundAdExpired
|
|
934
|
+
{
|
|
935
|
+
NSString *adUnitId = self.boundHolder.adUnitId ?: (self.adUnitId ?: @"");
|
|
936
|
+
[self clearRenderedAdSilently];
|
|
937
|
+
self.boundHolder = nil;
|
|
938
|
+
|
|
939
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdapterAdExpired
|
|
940
|
+
description: @"native ad expired"];
|
|
941
|
+
if ( self.onAdDisplayFailedEvent )
|
|
942
|
+
{
|
|
943
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: adUnitId error: error]);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
- (void)teardownPreloadedBinding
|
|
948
|
+
{
|
|
949
|
+
if ( self.boundHolder )
|
|
950
|
+
{
|
|
951
|
+
if ( self.boundHolder.boundView == self )
|
|
952
|
+
{
|
|
953
|
+
self.boundHolder.boundView = nil;
|
|
954
|
+
}
|
|
955
|
+
self.boundHolder = nil;
|
|
956
|
+
}
|
|
957
|
+
[self clearRenderedAdSilently];
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
- (void)bindPreloadedAdIfNeeded
|
|
961
|
+
{
|
|
962
|
+
if ( self.nativeAdId.length == 0 ) return;
|
|
963
|
+
|
|
964
|
+
CloudXNativeAdHolder *holder = [CloudXNativeAdHolder holderForId: self.nativeAdId];
|
|
965
|
+
if ( !holder )
|
|
966
|
+
{
|
|
967
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdNotReady
|
|
968
|
+
description: @"native ad destroyed"];
|
|
969
|
+
if ( self.onAdDisplayFailedEvent )
|
|
970
|
+
{
|
|
971
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: self.adUnitId ?: @"" error: error]);
|
|
972
|
+
}
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
if ( self.preloadedAdPresented && holder == self.boundHolder && holder.boundView == self ) return;
|
|
977
|
+
|
|
978
|
+
if ( holder.isExpired || (holder.ad && holder.ad.nativeAd && holder.ad.nativeAd.isExpired) )
|
|
979
|
+
{
|
|
980
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdapterAdExpired
|
|
981
|
+
description: @"native ad expired"];
|
|
982
|
+
if ( self.onAdDisplayFailedEvent )
|
|
983
|
+
{
|
|
984
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: holder.adUnitId ?: (self.adUnitId ?: @"") error: error]);
|
|
985
|
+
}
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
if ( !holder.ad )
|
|
990
|
+
{
|
|
991
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdNotReady
|
|
992
|
+
description: @"native ad destroyed"];
|
|
993
|
+
if ( self.onAdDisplayFailedEvent )
|
|
994
|
+
{
|
|
995
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: holder.adUnitId ?: (self.adUnitId ?: @"") error: error]);
|
|
996
|
+
}
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// Latest bind wins: if another view is currently bound, clear it silently
|
|
1001
|
+
CloudXNativeAdView *previousBound = holder.boundView;
|
|
1002
|
+
if ( previousBound && previousBound != self )
|
|
1003
|
+
{
|
|
1004
|
+
[previousBound clearRenderedAdSilently];
|
|
1005
|
+
}
|
|
1006
|
+
holder.boundView = self;
|
|
1007
|
+
self.boundHolder = holder;
|
|
1008
|
+
self.pendingManualAd = holder.ad;
|
|
1009
|
+
|
|
1010
|
+
if ( !self.manualRender )
|
|
1011
|
+
{
|
|
1012
|
+
BOOL rendered = [self presentTemplateAd: holder.ad withNativeAdView: nil loader: holder.loader];
|
|
1013
|
+
if ( !rendered )
|
|
1014
|
+
{
|
|
1015
|
+
[[CloudX shared] log: @"Preloaded native template render failed (ad expired or invalid) — emitting onAdDisplayFailedEvent. nativeAdId=%@", self.nativeAdId];
|
|
1016
|
+
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdapterAdExpired
|
|
1017
|
+
description: @"template render failed; ad expired or invalid"];
|
|
1018
|
+
if ( self.onAdDisplayFailedEvent )
|
|
1019
|
+
{
|
|
1020
|
+
self.onAdDisplayFailedEvent([self loadFailedDictForAdUnit: holder.adUnitId ?: (self.adUnitId ?: @"") error: error]);
|
|
1021
|
+
}
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
self.preloadedAdPresented = YES;
|
|
1026
|
+
if ( self.onAdDisplayedEvent )
|
|
1027
|
+
{
|
|
1028
|
+
self.onAdDisplayedEvent([self adInfoDictForAd: holder.ad]);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
else
|
|
1032
|
+
{
|
|
1033
|
+
self.currentTemplateAd = nil;
|
|
1034
|
+
self.preloadedAdPresented = YES;
|
|
1035
|
+
if ( self.onAdLoadedEvent )
|
|
1036
|
+
{
|
|
1037
|
+
self.onAdLoadedEvent([self adInfoDictForAd: holder.ad]);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
776
1042
|
#pragma mark - Event payloads
|
|
777
1043
|
|
|
778
1044
|
- (NSDictionary<NSString *, id> *)adInfoDictForAd:(CLXAd *)ad
|
|
@@ -856,27 +1122,12 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
856
1122
|
|
|
857
1123
|
if ( !self.manualRender )
|
|
858
1124
|
{
|
|
859
|
-
|
|
860
|
-
// from the publisher-selected template. renderNativeAdView:
|
|
861
|
-
// wires up impression and click tracking on the template's
|
|
862
|
-
// typed outlets.
|
|
863
|
-
CLXNativeAdView *view = nativeAdView;
|
|
864
|
-
BOOL rendered = YES;
|
|
865
|
-
if ( !view )
|
|
866
|
-
{
|
|
867
|
-
NSString *name = self.templateName.length > 0 ? self.templateName : @"medium_template";
|
|
868
|
-
view = [CLXNativeAdView viewFromAd: ad.nativeAd withTemplate: name];
|
|
869
|
-
rendered = loader ? [loader renderNativeAdView: view withAd: ad] : NO;
|
|
870
|
-
}
|
|
871
|
-
|
|
1125
|
+
BOOL rendered = [self presentTemplateAd: ad withNativeAdView: nativeAdView loader: loader];
|
|
872
1126
|
if ( !rendered )
|
|
873
1127
|
{
|
|
874
1128
|
// Template render failed — the ad is expired or invalid. Do
|
|
875
1129
|
// not add the view, do not fire onAdLoadedEvent; surface the
|
|
876
1130
|
// failure so it is observable.
|
|
877
|
-
[self.currentNativeView removeFromSuperview];
|
|
878
|
-
self.currentNativeView = nil;
|
|
879
|
-
self.currentTemplateAd = nil;
|
|
880
1131
|
[[CloudX shared] log: @"Native template render failed (ad expired or invalid) — emitting onAdLoadFailedEvent. adUnitId=%@", ad.adUnitId ?: @""];
|
|
881
1132
|
CLXError *error = [CLXError errorWithCode: CLXErrorCodeAdapterAdExpired
|
|
882
1133
|
description: @"template render failed; ad expired or invalid"];
|
|
@@ -884,18 +1135,7 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
884
1135
|
return;
|
|
885
1136
|
}
|
|
886
1137
|
|
|
887
|
-
|
|
888
|
-
self.currentNativeView = view;
|
|
889
|
-
self.currentTemplateAd = ad;
|
|
890
|
-
|
|
891
|
-
view.translatesAutoresizingMaskIntoConstraints = NO;
|
|
892
|
-
[self addSubview: view];
|
|
893
|
-
[NSLayoutConstraint activateConstraints: @[
|
|
894
|
-
[view.topAnchor constraintEqualToAnchor: self.topAnchor],
|
|
895
|
-
[view.bottomAnchor constraintEqualToAnchor: self.bottomAnchor],
|
|
896
|
-
[view.leadingAnchor constraintEqualToAnchor: self.leadingAnchor],
|
|
897
|
-
[view.trailingAnchor constraintEqualToAnchor: self.trailingAnchor],
|
|
898
|
-
]];
|
|
1138
|
+
if ( self.onAdDisplayedEvent ) self.onAdDisplayedEvent([self adInfoDictForAd: ad]);
|
|
899
1139
|
}
|
|
900
1140
|
else
|
|
901
1141
|
{
|
|
@@ -929,6 +1169,7 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
929
1169
|
|
|
930
1170
|
- (void)didClickNativeAd:(CLXAd *)ad
|
|
931
1171
|
{
|
|
1172
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
932
1173
|
// Snapshot the generation before the hop; see loadGeneration.
|
|
933
1174
|
NSUInteger generation = self.loadGeneration;
|
|
934
1175
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -939,6 +1180,7 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
939
1180
|
|
|
940
1181
|
- (void)didExpireNativeAd:(CLXAd *)ad
|
|
941
1182
|
{
|
|
1183
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
942
1184
|
// Snapshot the generation before the hop; see loadGeneration.
|
|
943
1185
|
NSUInteger generation = self.loadGeneration;
|
|
944
1186
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -949,6 +1191,7 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
949
1191
|
|
|
950
1192
|
- (void)didCloseNativeAd:(CLXAd *)ad
|
|
951
1193
|
{
|
|
1194
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
952
1195
|
// Snapshot the generation before the hop; see loadGeneration.
|
|
953
1196
|
NSUInteger generation = self.loadGeneration;
|
|
954
1197
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -961,6 +1204,7 @@ static UIView *clx_findDescendantWithReactTag(UIView *root, NSInteger reactTag)
|
|
|
961
1204
|
|
|
962
1205
|
- (void)didPayRevenueForAd:(CLXAd *)ad
|
|
963
1206
|
{
|
|
1207
|
+
if ( self.nativeAdId.length > 0 ) return;
|
|
964
1208
|
// Snapshot the generation before the hop; see loadGeneration.
|
|
965
1209
|
NSUInteger generation = self.loadGeneration;
|
|
966
1210
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -27,6 +27,11 @@ RCT_CUSTOM_VIEW_PROPERTY(adUnitId, NSString, CloudXNativeAdView)
|
|
|
27
27
|
view.adUnitId = (json == nil || json == (id)[NSNull null]) ? nil : [RCTConvert NSString: json];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
RCT_CUSTOM_VIEW_PROPERTY(nativeAdId, NSString, CloudXNativeAdView)
|
|
31
|
+
{
|
|
32
|
+
view.nativeAdId = (json == nil || json == (id)[NSNull null]) ? nil : [RCTConvert NSString: json];
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
RCT_CUSTOM_VIEW_PROPERTY(extraParameters, NSArray, CloudXNativeAdView)
|
|
31
36
|
{
|
|
32
37
|
NSArray<NSDictionary<NSString *, id> *> *entries = [json isKindOfClass: [NSArray class]] ? json : nil;
|
|
@@ -51,8 +56,10 @@ RCT_CUSTOM_VIEW_PROPERTY(template, NSString, CloudXNativeAdView)
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
RCT_EXPORT_VIEW_PROPERTY(onAdLoadedEvent, RCTDirectEventBlock)
|
|
59
|
+
RCT_EXPORT_VIEW_PROPERTY(onAdDisplayedEvent, RCTDirectEventBlock)
|
|
54
60
|
RCT_EXPORT_VIEW_PROPERTY(onAdLoadFailedEvent, RCTDirectEventBlock)
|
|
55
61
|
RCT_EXPORT_VIEW_PROPERTY(onAdDisplayFailedEvent, RCTDirectEventBlock)
|
|
62
|
+
RCT_EXPORT_VIEW_PROPERTY(onAdBindingClearedEvent, RCTDirectEventBlock)
|
|
56
63
|
RCT_EXPORT_VIEW_PROPERTY(onAdClickedEvent, RCTDirectEventBlock)
|
|
57
64
|
RCT_EXPORT_VIEW_PROPERTY(onAdExpiredEvent, RCTDirectEventBlock)
|
|
58
65
|
RCT_EXPORT_VIEW_PROPERTY(onAdClosedEvent, RCTDirectEventBlock)
|