cloudx-react-native 3.10.0 → 3.11.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 (105) hide show
  1. package/README.md +1 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +10 -0
  4. package/android/src/main/java/com/cloudxreactnative/CloudXAdInfo.kt +40 -0
  5. package/android/src/main/java/com/cloudxreactnative/CloudXNativeAdView.kt +718 -0
  6. package/android/src/main/java/com/cloudxreactnative/CloudXNativeAdViewManagerImpl.kt +119 -0
  7. package/android/src/main/java/com/cloudxreactnative/CloudXPackage.kt +4 -1
  8. package/android/src/main/res/layout/cloudx_rn_native_media_banner_template.xml +100 -0
  9. package/android/src/main/res/layout/cloudx_rn_native_medium_template.xml +99 -0
  10. package/android/src/main/res/layout/cloudx_rn_native_small_template.xml +72 -0
  11. package/android/src/main/res/layout/cloudx_rn_native_vertical_media_template.xml +117 -0
  12. package/android/src/newarch/com/cloudxreactnative/CloudXModule.kt +1 -0
  13. package/android/src/newarch/com/cloudxreactnative/CloudXNativeAdViewManager.kt +86 -0
  14. package/android/src/oldarch/com/cloudxreactnative/CloudXModule.kt +1 -0
  15. package/android/src/oldarch/com/cloudxreactnative/CloudXNativeAdViewManager.kt +118 -0
  16. package/cloudx-react-native.podspec +1 -1
  17. package/ios/CloudX.h +8 -0
  18. package/ios/CloudX.mm +159 -29
  19. package/ios/CloudXNativeAdView.h +75 -0
  20. package/ios/CloudXNativeAdView.mm +979 -0
  21. package/ios/CloudXNativeAdViewManager.h +12 -0
  22. package/ios/CloudXNativeAdViewManager.mm +119 -0
  23. package/lib/commonjs/CloudXAdViewBase.js +8 -97
  24. package/lib/commonjs/CloudXAdViewBase.js.map +1 -1
  25. package/lib/commonjs/CloudXNativeAdView.js +331 -0
  26. package/lib/commonjs/CloudXNativeAdView.js.map +1 -0
  27. package/lib/commonjs/CloudXNativeAdViewComponents.js +252 -0
  28. package/lib/commonjs/CloudXNativeAdViewComponents.js.map +1 -0
  29. package/lib/commonjs/CloudXNativeAdViewProvider.js +117 -0
  30. package/lib/commonjs/CloudXNativeAdViewProvider.js.map +1 -0
  31. package/lib/commonjs/extraParameters.js +52 -0
  32. package/lib/commonjs/extraParameters.js.map +1 -0
  33. package/lib/commonjs/hooks.js +34 -0
  34. package/lib/commonjs/hooks.js.map +1 -1
  35. package/lib/commonjs/index.js +72 -0
  36. package/lib/commonjs/index.js.map +1 -1
  37. package/lib/commonjs/specs/CloudXNativeAdViewNativeComponent.js +41 -0
  38. package/lib/commonjs/specs/CloudXNativeAdViewNativeComponent.js.map +1 -0
  39. package/lib/commonjs/types/NativeAd.js +70 -0
  40. package/lib/commonjs/types/NativeAd.js.map +1 -0
  41. package/lib/commonjs/types/index.js +13 -0
  42. package/lib/commonjs/types/index.js.map +1 -1
  43. package/lib/commonjs/useCloudXSdkInitialization.js +75 -0
  44. package/lib/commonjs/useCloudXSdkInitialization.js.map +1 -0
  45. package/lib/commonjs/version.js +1 -1
  46. package/lib/module/CloudXAdViewBase.js +7 -96
  47. package/lib/module/CloudXAdViewBase.js.map +1 -1
  48. package/lib/module/CloudXNativeAdView.js +324 -0
  49. package/lib/module/CloudXNativeAdView.js.map +1 -0
  50. package/lib/module/CloudXNativeAdViewComponents.js +240 -0
  51. package/lib/module/CloudXNativeAdViewComponents.js.map +1 -0
  52. package/lib/module/CloudXNativeAdViewProvider.js +110 -0
  53. package/lib/module/CloudXNativeAdViewProvider.js.map +1 -0
  54. package/lib/module/extraParameters.js +45 -0
  55. package/lib/module/extraParameters.js.map +1 -0
  56. package/lib/module/hooks.js +34 -0
  57. package/lib/module/hooks.js.map +1 -1
  58. package/lib/module/index.js +13 -0
  59. package/lib/module/index.js.map +1 -1
  60. package/lib/module/specs/CloudXNativeAdViewNativeComponent.js +35 -0
  61. package/lib/module/specs/CloudXNativeAdViewNativeComponent.js.map +1 -0
  62. package/lib/module/types/NativeAd.js +68 -0
  63. package/lib/module/types/NativeAd.js.map +1 -0
  64. package/lib/module/types/index.js +1 -0
  65. package/lib/module/types/index.js.map +1 -1
  66. package/lib/module/useCloudXSdkInitialization.js +68 -0
  67. package/lib/module/useCloudXSdkInitialization.js.map +1 -0
  68. package/lib/module/version.js +1 -1
  69. package/lib/typescript/src/CloudXAdViewBase.d.ts.map +1 -1
  70. package/lib/typescript/src/CloudXNativeAdView.d.ts +24 -0
  71. package/lib/typescript/src/CloudXNativeAdView.d.ts.map +1 -0
  72. package/lib/typescript/src/CloudXNativeAdViewComponents.d.ts +67 -0
  73. package/lib/typescript/src/CloudXNativeAdViewComponents.d.ts.map +1 -0
  74. package/lib/typescript/src/CloudXNativeAdViewProvider.d.ts +72 -0
  75. package/lib/typescript/src/CloudXNativeAdViewProvider.d.ts.map +1 -0
  76. package/lib/typescript/src/extraParameters.d.ts +4 -0
  77. package/lib/typescript/src/extraParameters.d.ts.map +1 -0
  78. package/lib/typescript/src/hooks.d.ts.map +1 -1
  79. package/lib/typescript/src/index.d.ts +4 -1
  80. package/lib/typescript/src/index.d.ts.map +1 -1
  81. package/lib/typescript/src/specs/CloudXNativeAdViewNativeComponent.d.ts +103 -0
  82. package/lib/typescript/src/specs/CloudXNativeAdViewNativeComponent.d.ts.map +1 -0
  83. package/lib/typescript/src/types/AdInfo.d.ts +4 -1
  84. package/lib/typescript/src/types/AdInfo.d.ts.map +1 -1
  85. package/lib/typescript/src/types/NativeAd.d.ts +114 -0
  86. package/lib/typescript/src/types/NativeAd.d.ts.map +1 -0
  87. package/lib/typescript/src/types/index.d.ts +2 -0
  88. package/lib/typescript/src/types/index.d.ts.map +1 -1
  89. package/lib/typescript/src/useCloudXSdkInitialization.d.ts +16 -0
  90. package/lib/typescript/src/useCloudXSdkInitialization.d.ts.map +1 -0
  91. package/lib/typescript/src/version.d.ts +1 -1
  92. package/package.json +1 -1
  93. package/src/CloudXAdViewBase.tsx +8 -125
  94. package/src/CloudXNativeAdView.tsx +419 -0
  95. package/src/CloudXNativeAdViewComponents.tsx +203 -0
  96. package/src/CloudXNativeAdViewProvider.tsx +149 -0
  97. package/src/extraParameters.ts +65 -0
  98. package/src/hooks.ts +38 -0
  99. package/src/index.ts +24 -0
  100. package/src/specs/CloudXNativeAdViewNativeComponent.ts +137 -0
  101. package/src/types/AdInfo.ts +5 -1
  102. package/src/types/NativeAd.ts +125 -0
  103. package/src/types/index.ts +7 -0
  104. package/src/useCloudXSdkInitialization.ts +73 -0
  105. package/src/version.ts +1 -1
@@ -0,0 +1,118 @@
1
+ package com.cloudxreactnative
2
+
3
+ import android.view.View
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import com.facebook.react.uimanager.ViewGroupManager
7
+ import com.facebook.react.uimanager.annotations.ReactProp
8
+
9
+ /**
10
+ * Old-Architecture (Paper) view manager for CloudXNativeAdView. Props are
11
+ * wired via [ReactProp] and commands via manual [receiveCommand] overloads
12
+ * (Paper has no codegen delegate); all logic delegates to the shared
13
+ * CloudXNativeAdViewManagerImpl.
14
+ */
15
+ class CloudXNativeAdViewManager : ViewGroupManager<CloudXNativeAdView>() {
16
+
17
+ private companion object {
18
+ const val COMMAND_LOAD_AD = 1
19
+ const val COMMAND_DESTROY_AD = 2
20
+ const val COMMAND_UPDATE_ASSET_VIEW = 3
21
+ const val COMMAND_RENDER_NATIVE_AD = 4
22
+ }
23
+
24
+ override fun getName(): String = CloudXNativeAdViewManagerImpl.NAME
25
+
26
+ override fun createViewInstance(reactContext: ThemedReactContext): CloudXNativeAdView =
27
+ CloudXNativeAdViewManagerImpl.createViewInstance(reactContext)
28
+
29
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
30
+ CloudXNativeAdViewManagerImpl.getExportedCustomDirectEventTypeConstants()
31
+
32
+ @ReactProp(name = "adUnitId")
33
+ fun setAdUnitId(view: CloudXNativeAdView, value: String?) = CloudXNativeAdViewManagerImpl.setAdUnitId(view, value)
34
+
35
+ @ReactProp(name = "templateName")
36
+ fun setTemplateName(view: CloudXNativeAdView, value: String?) = CloudXNativeAdViewManagerImpl.setTemplateName(view, value)
37
+
38
+ // Legacy alias matching the archived package's public prop name; the Old
39
+ // Architecture is not governed by the codegen props struct, so it can keep
40
+ // accepting `template` for publisher compatibility (parity with iOS).
41
+ @ReactProp(name = "template")
42
+ fun setTemplate(view: CloudXNativeAdView, value: String?) = CloudXNativeAdViewManagerImpl.setTemplateName(view, value)
43
+
44
+ @ReactProp(name = "placement")
45
+ fun setPlacement(view: CloudXNativeAdView, value: String?) = CloudXNativeAdViewManagerImpl.setPlacement(view, value)
46
+
47
+ @ReactProp(name = "customData")
48
+ fun setCustomData(view: CloudXNativeAdView, value: String?) = CloudXNativeAdViewManagerImpl.setCustomData(view, value)
49
+
50
+ @ReactProp(name = "extraParameters")
51
+ fun setExtraParameters(view: CloudXNativeAdView, value: ReadableArray?) = CloudXNativeAdViewManagerImpl.setExtraParameters(view, value)
52
+
53
+ @ReactProp(name = "localExtraParameters")
54
+ fun setLocalExtraParameters(view: CloudXNativeAdView, value: ReadableArray?) = CloudXNativeAdViewManagerImpl.setLocalExtraParameters(view, value)
55
+
56
+ @ReactProp(name = "disableVideoFullScreen", defaultBoolean = false)
57
+ fun setDisableVideoFullScreen(view: CloudXNativeAdView, value: Boolean) = CloudXNativeAdViewManagerImpl.setDisableVideoFullScreen(view, value)
58
+
59
+ @ReactProp(name = "startVideoUnmuted", defaultBoolean = false)
60
+ fun setStartVideoUnmuted(view: CloudXNativeAdView, value: Boolean) = CloudXNativeAdViewManagerImpl.setStartVideoUnmuted(view, value)
61
+
62
+ @ReactProp(name = "hideVideoMediaControls", defaultBoolean = false)
63
+ fun setHideVideoMediaControls(view: CloudXNativeAdView, value: Boolean) = CloudXNativeAdViewManagerImpl.setHideVideoMediaControls(view, value)
64
+
65
+ @ReactProp(name = "manualRender", defaultBoolean = false)
66
+ fun setManualRender(view: CloudXNativeAdView, value: Boolean) = CloudXNativeAdViewManagerImpl.setManualRender(view, value)
67
+
68
+ // React-children shadow accessors: see CloudXNativeAdViewManagerImpl for why
69
+ // the SDK-owned ad view must stay invisible to React's manageChildren.
70
+
71
+ override fun getChildCount(parent: CloudXNativeAdView): Int = CloudXNativeAdViewManagerImpl.getChildCount(parent)
72
+
73
+ override fun getChildAt(parent: CloudXNativeAdView, index: Int): View? = CloudXNativeAdViewManagerImpl.getChildAt(parent, index)
74
+
75
+ override fun addView(parent: CloudXNativeAdView, child: View, index: Int) = CloudXNativeAdViewManagerImpl.addView(parent, child, index)
76
+
77
+ override fun removeViewAt(parent: CloudXNativeAdView, index: Int) = CloudXNativeAdViewManagerImpl.removeViewAt(parent, index)
78
+
79
+ override fun removeAllViews(parent: CloudXNativeAdView) = CloudXNativeAdViewManagerImpl.removeAllViews(parent)
80
+
81
+ override fun getCommandsMap(): MutableMap<String, Int> =
82
+ mutableMapOf(
83
+ "loadAd" to COMMAND_LOAD_AD,
84
+ "destroyAd" to COMMAND_DESTROY_AD,
85
+ "updateAssetView" to COMMAND_UPDATE_ASSET_VIEW,
86
+ "renderNativeAd" to COMMAND_RENDER_NATIVE_AD,
87
+ )
88
+
89
+ override fun receiveCommand(view: CloudXNativeAdView, commandId: String?, args: ReadableArray?) {
90
+ when (commandId) {
91
+ "loadAd" -> CloudXNativeAdViewManagerImpl.loadAd(view)
92
+ "destroyAd" -> CloudXNativeAdViewManagerImpl.destroyAd(view)
93
+ "updateAssetView" -> {
94
+ if (args == null || args.size() < 2) return
95
+ CloudXNativeAdViewManagerImpl.updateAssetView(view, args.getInt(0), args.getString(1) ?: return)
96
+ }
97
+ "renderNativeAd" -> CloudXNativeAdViewManagerImpl.renderNativeAd(view)
98
+ }
99
+ }
100
+
101
+ @Deprecated("Use the String-based receiveCommand overload.")
102
+ override fun receiveCommand(view: CloudXNativeAdView, commandId: Int, args: ReadableArray?) {
103
+ when (commandId) {
104
+ COMMAND_LOAD_AD -> CloudXNativeAdViewManagerImpl.loadAd(view)
105
+ COMMAND_DESTROY_AD -> CloudXNativeAdViewManagerImpl.destroyAd(view)
106
+ COMMAND_UPDATE_ASSET_VIEW -> {
107
+ if (args == null || args.size() < 2) return
108
+ CloudXNativeAdViewManagerImpl.updateAssetView(view, args.getInt(0), args.getString(1) ?: return)
109
+ }
110
+ COMMAND_RENDER_NATIVE_AD -> CloudXNativeAdViewManagerImpl.renderNativeAd(view)
111
+ }
112
+ }
113
+
114
+ override fun onDropViewInstance(view: CloudXNativeAdView) {
115
+ CloudXNativeAdViewManagerImpl.onDropViewInstance(view)
116
+ super.onDropViewInstance(view)
117
+ }
118
+ }
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
14
14
 
15
15
  s.requires_arc = true
16
16
 
17
- s.dependency "CloudXCore", "~> 3.10.0"
17
+ s.dependency "CloudXCore", "~> 3.11.0"
18
18
 
19
19
  s.source_files = "ios/**/*.{h,mm}"
20
20
  s.resource_bundles = { 'CloudXReactNativePrivacy' => ['ios/PrivacyInfo.xcprivacy'] }
package/ios/CloudX.h CHANGED
@@ -42,6 +42,14 @@ NS_ASSUME_NONNULL_BEGIN
42
42
  */
43
43
  - (NSDictionary<NSString *, id> *)adDisplayFailedInfoForAd:(CLXAd *)ad withError:(CLXError *)error;
44
44
 
45
+ /**
46
+ * Builds the native ad's own metadata dictionary (title, media, icon, etc.) from a
47
+ * @c CLXAd . Exposed so the in-tree @c CloudXNativeAdView host and this class's
48
+ * generic ad-info builder share one source of truth for the shape of native ad
49
+ * metadata instead of duplicating the icon/media/options extraction logic.
50
+ */
51
+ - (NSDictionary<NSString *, id> *)nativeAdMetadataForAd:(CLXAd *)ad;
52
+
45
53
  @end
46
54
 
47
55
  NS_ASSUME_NONNULL_END
package/ios/CloudX.mm CHANGED
@@ -177,7 +177,7 @@ RCT_EXPORT_MODULE(CloudX)
177
177
  self.appOpenAds = [NSMutableDictionary dictionaryWithCapacity: 2];
178
178
  self.rewardedAds = [NSMutableDictionary dictionaryWithCapacity: 2];
179
179
 
180
- self.adViews = [NSMutableDictionary dictionaryWithCapacity: 2];
180
+ self.adViews = [NSMutableDictionary dictionaryWithCapacity: 2];
181
181
  self.adViewAdFormats = [NSMutableDictionary dictionaryWithCapacity: 2];
182
182
  self.adViewPositions = [NSMutableDictionary dictionaryWithCapacity: 2];
183
183
  self.adViewOffsets = [NSMutableDictionary dictionaryWithCapacity: 2];
@@ -190,6 +190,42 @@ RCT_EXPORT_MODULE(CloudX)
190
190
  return self;
191
191
  }
192
192
 
193
+ - (void)invalidate
194
+ {
195
+ [super invalidate];
196
+
197
+ dispatch_async( dispatch_get_main_queue(), ^{
198
+ for ( CLXAppOpen *appOpen in self.appOpenAds.allValues )
199
+ {
200
+ appOpen.delegate = nil;
201
+ [appOpen destroy];
202
+ }
203
+ [self.appOpenAds removeAllObjects];
204
+
205
+ for ( CLXInterstitial *interstitial in self.interstitials.allValues )
206
+ {
207
+ interstitial.delegate = nil;
208
+ [interstitial destroy];
209
+ }
210
+ [self.interstitials removeAllObjects];
211
+
212
+ for ( CLXRewarded *rewarded in self.rewardedAds.allValues )
213
+ {
214
+ rewarded.delegate = nil;
215
+ [rewarded destroy];
216
+ }
217
+ [self.rewardedAds removeAllObjects];
218
+
219
+ // Banner/MREC overlays: each ad unit owns a window-level CLXBannerAdView
220
+ // plus a safe-area background and per-unit maps. Without tearing these
221
+ // down here, bridge invalidation orphans the attached overlay views.
222
+ for ( NSString *adUnitIdentifier in self.adViews.allKeys )
223
+ {
224
+ [self teardownAdViewForAdUnitIdentifier: adUnitIdentifier];
225
+ }
226
+ } );
227
+ }
228
+
193
229
  #pragma mark - Initialization
194
230
 
195
231
  RCT_EXPORT_METHOD(isInitialized:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@@ -1188,32 +1224,41 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
1188
1224
  {
1189
1225
  [self executeOnMainQueue:^{
1190
1226
  [self log: @"Destroying %ld with ad unit identifier \"%@\"", (long)adFormat, adUnitIdentifier];
1227
+ [self teardownAdViewForAdUnitIdentifier: adUnitIdentifier];
1228
+ }];
1229
+ }
1191
1230
 
1192
- [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
1231
+ // Per-view overlay teardown shared by destroyAdViewWithAdUnitIdentifier: and
1232
+ // invalidate so bridge invalidation can't orphan banner/MREC overlays. Must be
1233
+ // called on the main queue. On the main queue retrieveAdViewForAdUnitIdentifier:
1234
+ // with a nil position returns exactly self.adViews[adUnitIdentifier], so reading
1235
+ // the map directly keeps the public method byte-identical.
1236
+ - (void)teardownAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier
1237
+ {
1238
+ [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
1193
1239
 
1194
- CLXBannerAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
1195
- view.delegate = nil;
1196
- view.revenueDelegate = nil;
1240
+ CLXBannerAdView *view = self.adViews[adUnitIdentifier];
1241
+ view.delegate = nil;
1242
+ view.revenueDelegate = nil;
1197
1243
 
1198
- NSArray<NSLayoutConstraint *> *activeConstraints = self.adViewConstraints[adUnitIdentifier];
1199
- [NSLayoutConstraint deactivateConstraints: activeConstraints];
1244
+ NSArray<NSLayoutConstraint *> *activeConstraints = self.adViewConstraints[adUnitIdentifier];
1245
+ [NSLayoutConstraint deactivateConstraints: activeConstraints];
1200
1246
 
1201
- [view destroy];
1202
- [view removeFromSuperview];
1247
+ [view destroy];
1248
+ [view removeFromSuperview];
1203
1249
 
1204
- UIView *safeAreaBg = self.safeAreaBackgrounds[adUnitIdentifier];
1205
- [NSLayoutConstraint deactivateConstraints: safeAreaBg.constraints];
1206
- [safeAreaBg removeFromSuperview];
1250
+ UIView *safeAreaBg = self.safeAreaBackgrounds[adUnitIdentifier];
1251
+ [NSLayoutConstraint deactivateConstraints: safeAreaBg.constraints];
1252
+ [safeAreaBg removeFromSuperview];
1207
1253
 
1208
- [self.adViews removeObjectForKey: adUnitIdentifier];
1209
- [self.adViewPositions removeObjectForKey: adUnitIdentifier];
1210
- [self.adViewOffsets removeObjectForKey: adUnitIdentifier];
1211
- [self.adViewAdFormats removeObjectForKey: adUnitIdentifier];
1212
- [self.adViewConstraints removeObjectForKey: adUnitIdentifier];
1213
- [self.adViewExtraParameters removeObjectForKey: adUnitIdentifier];
1214
- [self.safeAreaBackgrounds removeObjectForKey: adUnitIdentifier];
1215
- [self.publisherBannerBackgroundColors removeObjectForKey: adUnitIdentifier];
1216
- }];
1254
+ [self.adViews removeObjectForKey: adUnitIdentifier];
1255
+ [self.adViewPositions removeObjectForKey: adUnitIdentifier];
1256
+ [self.adViewOffsets removeObjectForKey: adUnitIdentifier];
1257
+ [self.adViewAdFormats removeObjectForKey: adUnitIdentifier];
1258
+ [self.adViewConstraints removeObjectForKey: adUnitIdentifier];
1259
+ [self.adViewExtraParameters removeObjectForKey: adUnitIdentifier];
1260
+ [self.safeAreaBackgrounds removeObjectForKey: adUnitIdentifier];
1261
+ [self.publisherBannerBackgroundColors removeObjectForKey: adUnitIdentifier];
1217
1262
  }
1218
1263
 
1219
1264
  - (CLXInterstitial *)retrieveInterstitialForAdUnitIdentifier:(NSString *)adUnitIdentifier
@@ -1732,14 +1777,98 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
1732
1777
 
1733
1778
  - (NSDictionary<NSString *, id> *)adInfoForAd:(CLXAd *)ad
1734
1779
  {
1735
- return @{@"adUnitId": ad.adUnitId ?: @"",
1736
- @"adFormat": [self adFormatStringForAd: ad],
1737
- @"networkName": ad.networkName ?: @"",
1738
- @"networkPlacement": ad.networkPlacement ?: @"",
1739
- @"placement": ad.placement ?: @"",
1740
- @"creativeId": ad.creativeIdentifier ?: @"",
1741
- @"revenue": ad.revenue ?: @(0),
1742
- @"adValues": ad.adValues ?: @{}};
1780
+ NSMutableDictionary<NSString *, id> *info = [@{@"adUnitId": ad.adUnitId ?: @"",
1781
+ @"adFormat": [self adFormatStringForAd: ad],
1782
+ @"networkName": ad.networkName ?: @"",
1783
+ @"networkPlacement": ad.networkPlacement ?: @"",
1784
+ @"placement": ad.placement ?: @"",
1785
+ @"creativeId": ad.creativeIdentifier ?: @"",
1786
+ @"revenue": ad.revenue ?: @(0),
1787
+ @"adValues": ad.adValues ?: @{}} mutableCopy];
1788
+
1789
+ if ( ad.nativeAd )
1790
+ {
1791
+ info[@"nativeAd"] = [self nativeAdMetadataForAd: ad];
1792
+ }
1793
+
1794
+ return info;
1795
+ }
1796
+
1797
+ // An icon crosses the RN bridge as a base64 PNG string, and this metadata is
1798
+ // rebuilt on every native-ad callback. Full-resolution artwork therefore costs a
1799
+ // multi-megabyte copy per event. 96pt matches the Android bridge's cap.
1800
+ static const CGFloat kCloudXNativeIconMaxDimension = 96.0;
1801
+
1802
+ - (nullable NSString *)base64PNGForIconImage:(UIImage *)image
1803
+ {
1804
+ CGFloat longestSide = MAX(image.size.width, image.size.height);
1805
+ UIImage *source = image;
1806
+
1807
+ if ( longestSide > kCloudXNativeIconMaxDimension && longestSide > 0 )
1808
+ {
1809
+ CGFloat scale = kCloudXNativeIconMaxDimension / longestSide;
1810
+ CGSize targetSize = CGSizeMake(round(image.size.width * scale), round(image.size.height * scale));
1811
+
1812
+ if ( targetSize.width >= 1 && targetSize.height >= 1 )
1813
+ {
1814
+ UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize: targetSize];
1815
+ source = [renderer imageWithActions: ^(UIGraphicsImageRendererContext *context) {
1816
+ [image drawInRect: CGRectMake(0, 0, targetSize.width, targetSize.height)];
1817
+ }];
1818
+ }
1819
+ }
1820
+
1821
+ NSData *imageData = UIImagePNGRepresentation(source);
1822
+ if ( !imageData )
1823
+ {
1824
+ NSLog(@"[%@] [%@] could not PNG-encode native ad icon of size %@", SDK_TAG, TAG, NSStringFromCGSize(source.size));
1825
+ return nil;
1826
+ }
1827
+
1828
+ return [imageData base64EncodedStringWithOptions: 0];
1829
+ }
1830
+
1831
+ - (NSDictionary<NSString *, id> *)nativeAdMetadataForAd:(CLXAd *)ad
1832
+ {
1833
+ CLXNativeAd *nativeAd = ad.nativeAd;
1834
+ NSMutableDictionary<NSString *, id> *info = [NSMutableDictionary dictionaryWithCapacity: 16];
1835
+
1836
+ // CloudXNativeAdMetadata declares these as `string | null`, and the Android
1837
+ // bridge sends null. Substituting @"" here made `title ?? fallback` resolve
1838
+ // differently per platform for an asset the creative simply does not carry.
1839
+ info[@"title"] = nativeAd.title ?: [NSNull null];
1840
+ info[@"advertiser"] = nativeAd.advertiser ?: [NSNull null];
1841
+ info[@"body"] = nativeAd.body ?: [NSNull null];
1842
+ info[@"callToAction"] = nativeAd.callToAction ?: [NSNull null];
1843
+ if ( nativeAd.starRating ) info[@"starRating"] = nativeAd.starRating;
1844
+
1845
+ if ( nativeAd.mediaContentAspectRatio > 0 )
1846
+ {
1847
+ info[@"mediaContentAspectRatio"] = @(nativeAd.mediaContentAspectRatio);
1848
+ }
1849
+
1850
+ BOOL iconAvailable = (nativeAd.icon != nil || nativeAd.iconView != nil);
1851
+ info[@"isIconImageAvailable"] = @(iconAvailable);
1852
+ info[@"isOptionsViewAvailable"] = @(nativeAd.optionsView != nil);
1853
+ info[@"isMediaViewAvailable"] = @(nativeAd.mediaView != nil);
1854
+
1855
+ if ( nativeAd.icon )
1856
+ {
1857
+ if ( nativeAd.icon.URL )
1858
+ {
1859
+ info[@"url"] = nativeAd.icon.URL.absoluteString;
1860
+ }
1861
+ else if ( nativeAd.icon.image )
1862
+ {
1863
+ info[@"imageSource"] = [self base64PNGForIconImage: nativeAd.icon.image] ?: [NSNull null];
1864
+ }
1865
+ }
1866
+
1867
+ info[@"isVideoContent"] = @(nativeAd.isVideoContent);
1868
+ info[@"videoDuration"] = @(nativeAd.videoDuration);
1869
+ info[@"isExpired"] = @(nativeAd.isExpired);
1870
+
1871
+ return info;
1743
1872
  }
1744
1873
 
1745
1874
  - (NSDictionary<NSString *, id> *)adLoadFailedInfoForAd:(NSString *)adUnitIdentifier withError:(nullable CLXError *)error
@@ -2038,6 +2167,7 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
2038
2167
  case CLXAdFormatMREC: return @"MREC";
2039
2168
  case CLXAdFormatInterstitial: return @"INTERSTITIAL";
2040
2169
  case CLXAdFormatAppOpen: return @"APP_OPEN";
2170
+ case CLXAdFormatNative: return @"NATIVE";
2041
2171
  case CLXAdFormatRewarded: return @"REWARDED";
2042
2172
  default: return @"";
2043
2173
  }
@@ -0,0 +1,75 @@
1
+ //
2
+ // CloudXNativeAdView.h
3
+ // cloudx-react-native
4
+ //
5
+ // In-tree native ad view component. Unlike CloudXAdView (banner/MREC), which
6
+ // hosts a single SDK-owned ad view, this host owns a dedicated
7
+ // CLXNativeAdLoader and renders either the SDK's bundled template views or a
8
+ // publisher-driven manual layout bound via the loadAd/updateAssetView/
9
+ // renderNativeAd commands.
10
+ //
11
+
12
+ #import <React/RCTUIManager.h>
13
+ #import <CloudXCore/CloudXCore.h>
14
+
15
+ #ifdef RCT_NEW_ARCH_ENABLED
16
+ #import <React/RCTViewComponentView.h>
17
+ @interface CloudXNativeAdView : RCTViewComponentView <CLXNativeAdDelegate, CLXAdRevenueDelegate>
18
+ #else
19
+ @interface CloudXNativeAdView : UIView <CLXNativeAdDelegate, CLXAdRevenueDelegate>
20
+ #endif
21
+
22
+ NS_ASSUME_NONNULL_BEGIN
23
+
24
+ @property (nonatomic, copy, nullable) NSString *adUnitId;
25
+ @property (nonatomic, copy, nullable) NSString *templateName;
26
+ @property (nonatomic, copy, nullable) NSString *placement;
27
+ @property (nonatomic, copy, nullable) NSString *customData;
28
+ @property (nonatomic, assign) BOOL disableVideoFullScreen;
29
+ @property (nonatomic, assign) BOOL startVideoUnmuted;
30
+ @property (nonatomic, assign) BOOL hideVideoMediaControls;
31
+
32
+ /// Controls how a loaded ad is rendered: when @c YES , the host skips
33
+ /// template view creation and lets the JS layer drive layout through the
34
+ /// @c updateAssetView + @c renderNativeAd commands. Auto-load on window
35
+ /// attachment runs in both modes.
36
+ @property (nonatomic, assign) BOOL manualRender;
37
+
38
+ // Nil by default on the Old Architecture (only set when the publisher passes
39
+ // the corresponding callback); delegate call sites guard with `if (self.onX)`.
40
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdLoadedEvent;
41
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdLoadFailedEvent;
42
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdDisplayFailedEvent;
43
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdClickedEvent;
44
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdExpiredEvent;
45
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdClosedEvent;
46
+ @property (nonatomic, copy, nullable) RCTDirectEventBlock onAdRevenuePaidEvent;
47
+
48
+ - (void)setExtraParametersFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)extraParameters;
49
+ - (void)setLocalExtraParametersFromEntryArray:(nullable NSArray<NSDictionary<NSString *, id> *> *)localExtraParameters;
50
+
51
+ #pragma mark - Commands
52
+ //
53
+ // These selectors are named to match RCTCloudXNativeAdViewViewProtocol
54
+ // (react/renderer/components/CloudXSpec/RCTComponentViewHelpers.h) exactly, so
55
+ // the same implementations serve both the Fabric command-dispatch protocol
56
+ // and the Old Architecture manager's UIManager-block command handlers.
57
+
58
+ /// Forces a (re)load, bypassing the "already requested" auto-load guard.
59
+ - (void)loadAd;
60
+
61
+ /// Destroys the current loaded ad(s) and loader state. Safe to call multiple
62
+ /// times; a no-op when nothing is loaded.
63
+ - (void)destroyAd;
64
+
65
+ /// Binds a publisher-supplied asset subview (resolved by descendant tag
66
+ /// lookup within this host) to a named asset role for manual rendering.
67
+ - (void)updateAssetView:(NSInteger)assetViewTag assetName:(NSString *)assetName;
68
+
69
+ /// Registers click/impression tracking against the publisher's manually
70
+ /// bound asset views and fires onAdLoaded/onAdDisplayFailed accordingly.
71
+ - (void)renderNativeAd;
72
+
73
+ @end
74
+
75
+ NS_ASSUME_NONNULL_END