@sellwild/react-native-sdk 1.2.0 → 1.3.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 (133) hide show
  1. package/android/build/.transforms/055edc6990905b0d0e7b2a38f451ea8f/results.bin +1 -0
  2. package/android/build/.transforms/055edc6990905b0d0e7b2a38f451ea8f/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/.transforms/9adcec60aa21840716976bd52bc3bd4a/results.bin +1 -0
  4. package/android/build/.transforms/9adcec60aa21840716976bd52bc3bd4a/transformed/classes/classes_dex/classes.dex +0 -0
  5. package/android/build/.transforms/f6cdcb296ca6520e70a1cebbdcbe808e/results.bin +1 -0
  6. package/android/build/.transforms/f6cdcb296ca6520e70a1cebbdcbe808e/transformed/classes/classes_dex/classes.dex +0 -0
  7. package/android/build/generated/source/buildConfig/debug/com/sellwild/rnsdk/BuildConfig.java +10 -0
  8. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/AndroidManifest.xml +7 -0
  9. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/output-metadata.json +18 -0
  10. package/android/build/intermediates/aar_metadata/debug/aar-metadata.properties +6 -0
  11. package/android/build/intermediates/annotation_processor_list/debug/annotationProcessors.json +1 -0
  12. package/android/build/intermediates/compile_library_classes_jar/debug/classes.jar +0 -0
  13. package/android/build/intermediates/compile_r_class_jar/debug/R.jar +0 -0
  14. package/android/build/intermediates/compile_symbol_list/debug/R.txt +0 -0
  15. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
  16. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  17. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  18. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  19. package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +2 -0
  20. package/android/build/intermediates/java_res/debug/out/META-INF/sellwild-react-native-sdk_debug.kotlin_module +0 -0
  21. package/android/build/intermediates/javac/debug/classes/com/sellwild/rnsdk/BuildConfig.class +0 -0
  22. package/android/build/intermediates/local_only_symbol_list/debug/R-def.txt +2 -0
  23. package/android/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt +7 -0
  24. package/android/build/intermediates/merged_manifest/debug/AndroidManifest.xml +7 -0
  25. package/android/build/intermediates/navigation_json/debug/navigation.json +1 -0
  26. package/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar +0 -0
  27. package/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt +1 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +2 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  109. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  110. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  111. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  112. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  113. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  114. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  115. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  116. package/android/build/outputs/logs/manifest-merger-debug-report.txt +16 -0
  117. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  118. package/android/build/tmp/kotlin-classes/debug/META-INF/sellwild-react-native-sdk_debug.kotlin_module +0 -0
  119. package/android/build/tmp/kotlin-classes/debug/com/sellwild/rnsdk/SellwildBannerViewManager$Companion.class +0 -0
  120. package/android/build/tmp/kotlin-classes/debug/com/sellwild/rnsdk/SellwildBannerViewManager$PendingProps.class +0 -0
  121. package/android/build/tmp/kotlin-classes/debug/com/sellwild/rnsdk/SellwildBannerViewManager$createViewInstance$1.class +0 -0
  122. package/android/build/tmp/kotlin-classes/debug/com/sellwild/rnsdk/SellwildBannerViewManager.class +0 -0
  123. package/android/build/tmp/kotlin-classes/debug/com/sellwild/rnsdk/SellwildSdkPackage.class +0 -0
  124. package/android/build.gradle +68 -0
  125. package/android/src/main/AndroidManifest.xml +2 -0
  126. package/android/src/main/java/com/sellwild/rnsdk/SellwildBannerViewManager.kt +200 -0
  127. package/android/src/main/java/com/sellwild/rnsdk/SellwildSdkPackage.kt +18 -0
  128. package/ios/SellwildBannerViewManager.m +24 -0
  129. package/ios/SellwildBannerViewManager.swift +168 -0
  130. package/ios/SellwildSDK-RN.podspec +31 -0
  131. package/package.json +2 -2
  132. package/src/SellwildBanner.tsx +105 -39
  133. package/src/htmlBuilder.ts +1 -1
@@ -0,0 +1,200 @@
1
+ package com.sellwild.rnsdk
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.common.MapBuilder
6
+ import com.facebook.react.uimanager.SimpleViewManager
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.facebook.react.uimanager.annotations.ReactProp
9
+ import com.facebook.react.uimanager.events.RCTEventEmitter
10
+ import com.sellwild.sdk.AdSize
11
+ import com.sellwild.sdk.SellwildAdView
12
+ import com.sellwild.sdk.SellwildConfig
13
+ import org.json.JSONObject
14
+
15
+ /**
16
+ * Bridges the JS <SellwildBanner> component to the native
17
+ * com.sellwild.sdk.SellwildAdView.
18
+ *
19
+ * Props (set from JS):
20
+ * - config: object — the resolved SellwildConfig (from configure()).
21
+ * Only the fields the native ad path reads are required:
22
+ * partnerCode, appBundleId, appStoreUrl, gamTag, debug,
23
+ * adRefreshMax, adRefreshMaxMobile, adRefreshIntervalMs,
24
+ * prebidServer (object), remote (object).
25
+ * - size: string — "320x50", "300x250", "728x90", "300x600", "160x600".
26
+ * - zoneId: string — Sellwild zone tag, e.g. "43".
27
+ *
28
+ * Events emitted to JS:
29
+ * - onAdLoaded
30
+ * - onAdImpression
31
+ * - onAdClicked
32
+ * - onAdFailed { message }
33
+ *
34
+ * The view defers calling SellwildAdView.setup() + load() until all three
35
+ * required props (config, size, zoneId) have arrived in a single transaction.
36
+ */
37
+ class SellwildBannerViewManager : SimpleViewManager<SellwildAdView>() {
38
+
39
+ override fun getName(): String = REACT_CLASS
40
+
41
+ /**
42
+ * Per-view scratch state. We can't call SellwildAdView.setup() until
43
+ * all three required props (config/size/zoneId) have arrived, so we
44
+ * stash them here and apply on onAfterUpdateTransaction.
45
+ *
46
+ * Keyed by the ad view itself so multiple <SellwildBanner>s on screen
47
+ * don't collide. Cleared on view drop.
48
+ */
49
+ private val pending = java.util.WeakHashMap<SellwildAdView, PendingProps>()
50
+
51
+ private data class PendingProps(
52
+ var config: ReadableMap? = null,
53
+ var size: String? = null,
54
+ var zoneId: String? = null,
55
+ // Guards against running fresh auctions on every JS re-render. We
56
+ // only call setup() + load() once per identity tuple. Refresh of
57
+ // the rendered ad is driven by the SDK's internal timer.
58
+ var lastAppliedKey: String? = null,
59
+ )
60
+
61
+ override fun createViewInstance(reactContext: ThemedReactContext): SellwildAdView {
62
+ val view = SellwildAdView(reactContext)
63
+ pending[view] = PendingProps()
64
+ view.listener = object : SellwildAdView.Listener {
65
+ override fun onAdLoaded(adView: SellwildAdView) {
66
+ emit(reactContext, adView, "onAdLoaded", null)
67
+ }
68
+
69
+ override fun onAdImpression(adView: SellwildAdView, zoneId: String) {
70
+ val payload = com.facebook.react.bridge.Arguments.createMap().apply {
71
+ putString("zoneId", zoneId)
72
+ }
73
+ emit(reactContext, adView, "onAdImpression", payload)
74
+ }
75
+
76
+ override fun onAdClicked(adView: SellwildAdView) {
77
+ emit(reactContext, adView, "onAdClicked", null)
78
+ }
79
+
80
+ override fun onAdFailed(adView: SellwildAdView, message: String) {
81
+ val payload = com.facebook.react.bridge.Arguments.createMap().apply {
82
+ putString("message", message)
83
+ }
84
+ emit(reactContext, adView, "onAdFailed", payload)
85
+ }
86
+ }
87
+ return view
88
+ }
89
+
90
+ @ReactProp(name = "config")
91
+ fun setConfig(view: SellwildAdView, value: ReadableMap?) {
92
+ pendingFor(view).config = value
93
+ }
94
+
95
+ @ReactProp(name = "size")
96
+ fun setSize(view: SellwildAdView, value: String?) {
97
+ pendingFor(view).size = value
98
+ }
99
+
100
+ @ReactProp(name = "zoneId")
101
+ fun setZoneId(view: SellwildAdView, value: String?) {
102
+ pendingFor(view).zoneId = value
103
+ }
104
+
105
+ override fun onAfterUpdateTransaction(view: SellwildAdView) {
106
+ super.onAfterUpdateTransaction(view)
107
+
108
+ val p = pending[view] ?: return
109
+ val configMap = p.config ?: return
110
+ val sizeLabel = p.size ?: return
111
+ val zoneId = p.zoneId ?: return
112
+
113
+ val adSize = adSizeFromLabel(sizeLabel) ?: return
114
+
115
+ // Skip if the props identity hasn't changed since last apply.
116
+ val key = "$sizeLabel|$zoneId|${configMap.hashCode()}"
117
+ if (p.lastAppliedKey == key) return
118
+ p.lastAppliedKey = key
119
+
120
+ val config = configFromMap(configMap)
121
+ view.setup(config, adSize, zoneId)
122
+ view.load()
123
+ }
124
+
125
+ override fun onDropViewInstance(view: SellwildAdView) {
126
+ pending.remove(view)
127
+ super.onDropViewInstance(view)
128
+ }
129
+
130
+ private fun pendingFor(view: SellwildAdView): PendingProps =
131
+ pending.getOrPut(view) { PendingProps() }
132
+
133
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
134
+ return MapBuilder.builder<String, Any>()
135
+ .put("onAdLoaded", MapBuilder.of("registrationName", "onAdLoaded"))
136
+ .put("onAdImpression", MapBuilder.of("registrationName", "onAdImpression"))
137
+ .put("onAdClicked", MapBuilder.of("registrationName", "onAdClicked"))
138
+ .put("onAdFailed", MapBuilder.of("registrationName", "onAdFailed"))
139
+ .build()
140
+ }
141
+
142
+ private fun emit(
143
+ context: ReactContext,
144
+ view: SellwildAdView,
145
+ name: String,
146
+ payload: com.facebook.react.bridge.WritableMap?,
147
+ ) {
148
+ context.getJSModule(RCTEventEmitter::class.java)
149
+ .receiveEvent(view.id, name, payload)
150
+ }
151
+
152
+ companion object {
153
+ const val REACT_CLASS = "SellwildBannerView"
154
+
155
+ internal fun adSizeFromLabel(label: String): AdSize? = when (label) {
156
+ "320x50" -> AdSize.BANNER_320x50
157
+ "300x250" -> AdSize.MREC_300x250
158
+ "728x90" -> AdSize.LEADERBOARD_728x90
159
+ "300x600" -> AdSize.HALF_PAGE_300x600
160
+ "160x600" -> AdSize.WIDE_SKYSCRAPER_160x600
161
+ else -> null
162
+ }
163
+
164
+ /**
165
+ * Build a minimal SellwildConfig from a JS map. Only the fields the
166
+ * native banner path actually reads are mapped here; everything else
167
+ * gets sane defaults from the data class. The raw CDN JSON (if
168
+ * present under `remote`) is preserved as `remoteJson` for the
169
+ * passthrough auction params.
170
+ */
171
+ internal fun configFromMap(map: ReadableMap): SellwildConfig {
172
+ val partnerCode = if (map.hasKey("partnerCode")) map.getString("partnerCode") else null
173
+ val appBundleId = if (map.hasKey("appBundleId")) map.getString("appBundleId") else null
174
+ val appStoreUrl = if (map.hasKey("appStoreUrl")) map.getString("appStoreUrl") else null
175
+ val gamTag = if (map.hasKey("gamTag")) map.getString("gamTag") else null
176
+ val debug = map.hasKey("debug") && map.getBoolean("debug")
177
+ val adRefreshMax = if (map.hasKey("adRefreshMax")) map.getInt("adRefreshMax") else 0
178
+ val adRefreshMaxMobile = if (map.hasKey("adRefreshMaxMobile")) map.getInt("adRefreshMaxMobile") else 0
179
+ val adRefreshIntervalMs = if (map.hasKey("adRefreshIntervalMs")) map.getDouble("adRefreshIntervalMs").toLong() else 30_000L
180
+
181
+ val remoteJson = if (map.hasKey("remote") && !map.isNull("remote")) {
182
+ JSONObject(map.getMap("remote")!!.toHashMap()).toString()
183
+ } else {
184
+ null
185
+ }
186
+
187
+ return SellwildConfig(
188
+ partnerCode = partnerCode ?: "",
189
+ appBundleId = appBundleId,
190
+ appStoreUrl = appStoreUrl,
191
+ gamTag = gamTag,
192
+ debug = debug,
193
+ adRefreshMax = adRefreshMax,
194
+ adRefreshMaxMobile = adRefreshMaxMobile,
195
+ adRefreshIntervalMs = adRefreshIntervalMs,
196
+ remoteJson = remoteJson,
197
+ )
198
+ }
199
+ }
200
+ }
@@ -0,0 +1,18 @@
1
+ package com.sellwild.rnsdk
2
+
3
+ import android.view.View
4
+ import com.facebook.react.ReactPackage
5
+ import com.facebook.react.bridge.NativeModule
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.uimanager.ReactShadowNode
8
+ import com.facebook.react.uimanager.ViewManager
9
+
10
+ class SellwildSdkPackage : ReactPackage {
11
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = emptyList()
12
+
13
+ override fun createViewManagers(
14
+ reactContext: ReactApplicationContext,
15
+ ): List<ViewManager<out View, out ReactShadowNode<*>>> = listOf(
16
+ SellwildBannerViewManager(),
17
+ )
18
+ }
@@ -0,0 +1,24 @@
1
+ #import <React/RCTViewManager.h>
2
+ #import <React/RCTUIManager.h>
3
+ #import <React/RCTBridgeModule.h>
4
+
5
+ // Register the view manager class with React Native. We export it under
6
+ // the JS name "SellwildBannerView" to match the Android side
7
+ // (com.sellwild.rnsdk.SellwildBannerViewManager.REACT_CLASS).
8
+ @interface RCT_EXTERN_REMAP_MODULE(SellwildBannerView,
9
+ SellwildBannerViewManager,
10
+ RCTViewManager)
11
+
12
+ // Props set from JS — these forward to @objc properties on
13
+ // SellwildBannerHostView.
14
+ RCT_EXPORT_VIEW_PROPERTY(config, NSDictionary)
15
+ RCT_EXPORT_VIEW_PROPERTY(size, NSString)
16
+ RCT_EXPORT_VIEW_PROPERTY(zoneId, NSString)
17
+
18
+ // Direct events emitted to JS callbacks.
19
+ RCT_EXPORT_VIEW_PROPERTY(onAdLoaded, RCTDirectEventBlock)
20
+ RCT_EXPORT_VIEW_PROPERTY(onAdImpression, RCTDirectEventBlock)
21
+ RCT_EXPORT_VIEW_PROPERTY(onAdClicked, RCTDirectEventBlock)
22
+ RCT_EXPORT_VIEW_PROPERTY(onAdFailed, RCTDirectEventBlock)
23
+
24
+ @end
@@ -0,0 +1,168 @@
1
+ import Foundation
2
+ import React
3
+ import SellwildSDK
4
+ import UIKit
5
+
6
+ /// Bridges the JS `<SellwildBanner>` component to the native
7
+ /// `SellwildAdView` (Prebid Mobile + AdManagerBannerView). Mirrors the
8
+ /// Android `SellwildBannerViewManager` shape.
9
+ ///
10
+ /// Props (set from JS):
11
+ /// - config: object — the resolved SellwildConfig (from configure()).
12
+ /// Only the fields the native ad path reads are required.
13
+ /// - size: string — "320x50", "300x250", "728x90", "300x600", "160x600".
14
+ /// - zoneId: string — Sellwild zone tag, e.g. "43".
15
+ ///
16
+ /// Events emitted to JS:
17
+ /// - onAdLoaded
18
+ /// - onAdImpression { zoneId }
19
+ /// - onAdClicked
20
+ /// - onAdFailed { message }
21
+ @objc(SellwildBannerViewManager)
22
+ public final class SellwildBannerViewManager: RCTViewManager {
23
+
24
+ public override static func requiresMainQueueSetup() -> Bool { true }
25
+
26
+ public override func view() -> UIView! {
27
+ return SellwildBannerHostView()
28
+ }
29
+ }
30
+
31
+ /// The actual UIView that hosts a `SellwildAdView`. We can't construct
32
+ /// `SellwildAdView` until all three required props (config / size /
33
+ /// zoneId) have arrived, so we cache them and apply once on
34
+ /// `didSetProps:`.
35
+ final class SellwildBannerHostView: UIView, SellwildAdViewDelegate {
36
+
37
+ // MARK: RN-set props
38
+
39
+ @objc var config: NSDictionary? { didSet { needsApply = true } }
40
+ @objc var size: NSString? { didSet { needsApply = true } }
41
+ @objc var zoneId: NSString? { didSet { needsApply = true } }
42
+
43
+ // MARK: RN events
44
+
45
+ @objc var onAdLoaded: RCTDirectEventBlock?
46
+ @objc var onAdImpression: RCTDirectEventBlock?
47
+ @objc var onAdClicked: RCTDirectEventBlock?
48
+ @objc var onAdFailed: RCTDirectEventBlock?
49
+
50
+ // MARK: Internals
51
+
52
+ private var adView: SellwildAdView?
53
+ private var lastAppliedKey: String?
54
+ private var needsApply: Bool = false
55
+
56
+ override init(frame: CGRect) {
57
+ super.init(frame: frame)
58
+ }
59
+
60
+ required init?(coder: NSCoder) { fatalError("init(coder:) not used") }
61
+
62
+ // RN calls didSetProps once per JS render after all @objc setters
63
+ // have run, which lets us batch setup() + load() into a single
64
+ // native call instead of firing one per prop change.
65
+ override func didSetProps(_ changedProps: [String]) {
66
+ guard needsApply else { return }
67
+ needsApply = false
68
+ applyIfReady()
69
+ }
70
+
71
+ private func applyIfReady() {
72
+ guard
73
+ let cfgMap = config,
74
+ let sizeStr = size as String?,
75
+ let zid = zoneId as String?,
76
+ let adSize = AdSize(rawValue: sizeStr)
77
+ else { return }
78
+
79
+ // Skip if the props identity hasn't changed since last apply —
80
+ // refresh of the rendered ad is driven by the SDK's internal
81
+ // timer, not by JS re-renders.
82
+ let key = "\(sizeStr)|\(zid)|\(cfgMap.hash)"
83
+ if lastAppliedKey == key { return }
84
+ lastAppliedKey = key
85
+
86
+ let sellwildConfig = Self.configFromMap(cfgMap)
87
+
88
+ // Tear down any previous ad view; SellwildAdView itself does not
89
+ // support adUnitId reassignment after load.
90
+ adView?.removeFromSuperview()
91
+
92
+ let view = SellwildAdView(config: sellwildConfig, adSize: adSize, zoneId: zid)
93
+ view.delegate = self
94
+ view.translatesAutoresizingMaskIntoConstraints = false
95
+ addSubview(view)
96
+ NSLayoutConstraint.activate([
97
+ view.leadingAnchor.constraint(equalTo: leadingAnchor),
98
+ view.topAnchor.constraint(equalTo: topAnchor),
99
+ view.widthAnchor.constraint(equalToConstant: adSize.cgSize.width),
100
+ view.heightAnchor.constraint(equalToConstant: adSize.cgSize.height),
101
+ ])
102
+ adView = view
103
+ view.load()
104
+ }
105
+
106
+ // MARK: SellwildAdViewDelegate
107
+
108
+ func sellwildAdViewDidLoad(_ adView: SellwildAdView) {
109
+ onAdLoaded?([:])
110
+ }
111
+
112
+ func sellwildAdView(_ adView: SellwildAdView, didReceiveImpressionForZoneId zoneId: String) {
113
+ onAdImpression?(["zoneId": zoneId])
114
+ }
115
+
116
+ func sellwildAdViewDidRecordClick(_ adView: SellwildAdView) {
117
+ onAdClicked?([:])
118
+ }
119
+
120
+ func sellwildAdView(_ adView: SellwildAdView, didFailWithError error: Error) {
121
+ onAdFailed?(["message": error.localizedDescription])
122
+ }
123
+
124
+ // MARK: Config marshalling
125
+
126
+ /// Build a minimal SellwildConfig from a JS dictionary. Only the
127
+ /// fields the native banner path actually reads are mapped; the
128
+ /// rest get the data class defaults. The raw CDN JSON (if present
129
+ /// under `remote`) is preserved as `remoteJSON` for passthrough.
130
+ static func configFromMap(_ map: NSDictionary) -> SellwildConfig {
131
+ let partnerCode = (map["partnerCode"] as? String) ?? ""
132
+
133
+ var cfg = SellwildConfig(partnerCode: partnerCode)
134
+
135
+ if let v = map["appBundleId"] as? String { cfg.appBundleId = v }
136
+ if let v = map["appStoreUrl"] as? String { cfg.appStoreUrl = v }
137
+ if let v = map["gamTag"] as? String { cfg.gamTag = v }
138
+ if let v = map["debug"] as? Bool { cfg.debug = v }
139
+ if let v = map["adRefreshMax"] as? Int { cfg.adRefreshMax = v }
140
+ if let v = map["adRefreshMaxMobile"] as? Int { cfg.adRefreshMaxMobile = v }
141
+
142
+ // JS bridge passes ms; iOS API is seconds (TimeInterval).
143
+ if let v = map["adRefreshIntervalMs"] as? NSNumber {
144
+ cfg.adRefreshInterval = v.doubleValue / 1000.0
145
+ }
146
+
147
+ if let prebid = map["prebidServer"] as? NSDictionary,
148
+ let accountId = prebid["accountId"] as? String,
149
+ let endpoint = prebid["endpoint"] as? String {
150
+ let bidders = (prebid["bidders"] as? [String]) ?? []
151
+ let timeout = (prebid["timeout"] as? Int) ?? 1500
152
+ cfg.prebidServer = PrebidServerConfig(
153
+ accountId: accountId,
154
+ endpoint: endpoint,
155
+ bidders: bidders,
156
+ timeout: timeout,
157
+ syncEndpoint: prebid["syncEndpoint"] as? String
158
+ )
159
+ }
160
+
161
+ if let remote = map["remote"] as? NSDictionary,
162
+ let data = try? JSONSerialization.data(withJSONObject: remote, options: []) {
163
+ cfg.remoteJSON = data
164
+ }
165
+
166
+ return cfg
167
+ }
168
+ }
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'SellwildSDK-RN'
7
+ s.version = package['version']
8
+ s.summary = 'React Native bridge for the Sellwild iOS SDK'
9
+ s.description = <<-DESC
10
+ Bridges <SellwildBanner> from @sellwild/react-native-sdk to the
11
+ native iOS SellwildAdView (Prebid Mobile + AdManagerBannerView).
12
+ There is no WebView in the ad path.
13
+ DESC
14
+
15
+ s.homepage = 'https://github.com/Antengo/sellwild-sdk'
16
+ s.license = { :type => 'MIT' }
17
+ s.author = { 'Sellwild' => 'sdk@sellwild.com' }
18
+ s.source = { :git => 'https://github.com/Antengo/sellwild-sdk.git', :tag => s.version.to_s }
19
+
20
+ s.platforms = { :ios => '13.0' }
21
+ s.swift_version = '5.5'
22
+
23
+ s.source_files = '*.{swift,m,h}'
24
+ s.requires_arc = true
25
+
26
+ # The host app must already pull in SellwildSDK (the iOS SDK pod). We
27
+ # depend on it explicitly so `pod install` resolves both with one
28
+ # declaration in the host Podfile.
29
+ s.dependency 'SellwildSDK', '>= 1.3.0'
30
+ s.dependency 'React-Core'
31
+ end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellwild/react-native-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Sellwild mobile advertising SDK for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -23,7 +23,7 @@
23
23
  "react-native-webview": ">=11.0.0"
24
24
  },
25
25
  "dependencies": {
26
- "@sellwild/sdk-core": "^1.2.0"
26
+ "@sellwild/sdk-core": "^1.3.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/react": "^18.0.0",
@@ -1,12 +1,9 @@
1
- import React, { useCallback, useState } from 'react'
2
- import { View, StyleSheet, ViewStyle } from 'react-native'
3
- import { WebView } from 'react-native-webview'
4
- import type { WebViewMessageEvent } from 'react-native-webview'
1
+ import React from 'react'
2
+ import { Platform, requireNativeComponent, UIManager, ViewStyle, View, Text, StyleSheet, NativeSyntheticEvent } from 'react-native'
5
3
  import type { SellwildConfig, AdSize } from '@sellwild/sdk-core'
6
- import { WIDGET_BASE_URL } from '@sellwild/sdk-core'
7
- import { buildBannerHtml } from './htmlBuilder'
8
4
 
9
- // Standard IAB mobile ad sizes
5
+ // Standard IAB mobile ad sizes — used to lock the host View dimensions.
6
+ // The actual ad size is also propagated to native as the `size` prop label.
10
7
  const AD_DIMENSIONS: Record<AdSize, { width: number; height: number }> = {
11
8
  '300x250': { width: 300, height: 250 },
12
9
  '320x50': { width: 320, height: 50 },
@@ -16,6 +13,42 @@ const AD_DIMENSIONS: Record<AdSize, { width: number; height: number }> = {
16
13
  '1x1': { width: 1, height: 1 },
17
14
  }
18
15
 
16
+ // ─── Native component bridge ─────────────────────────────────────────────────
17
+ //
18
+ // As of 1.3.0 the React Native banner is backed by a real native ad view —
19
+ // `com.sellwild.sdk.SellwildAdView` on Android (Prebid Mobile + AdManagerAdView)
20
+ // and `SellwildAdView` on iOS (Prebid Mobile + AdManagerBannerView). There is
21
+ // no WebView in the ad path on either platform.
22
+ //
23
+ // The Android bridge lives in `react-native/android` and is autolinked via
24
+ // the `@sellwild/react-native-sdk` package.
25
+
26
+ const NATIVE_NAME = 'SellwildBannerView'
27
+
28
+ interface NativeBannerProps {
29
+ config: object
30
+ size: string
31
+ zoneId: string
32
+ style?: ViewStyle
33
+ onAdLoaded?: (e: NativeSyntheticEvent<{}>) => void
34
+ onAdImpression?: (e: NativeSyntheticEvent<{ zoneId: string }>) => void
35
+ onAdClicked?: (e: NativeSyntheticEvent<{}>) => void
36
+ onAdFailed?: (e: NativeSyntheticEvent<{ message: string }>) => void
37
+ }
38
+
39
+ const NativeBanner = (() => {
40
+ // requireNativeComponent crashes loudly if the view manager isn't
41
+ // registered. Probe first so we can render a friendly fallback on
42
+ // platforms where the bridge isn't in this build yet.
43
+ const config = UIManager.getViewManagerConfig?.(NATIVE_NAME)
44
+ if (!config) {
45
+ return null
46
+ }
47
+ return requireNativeComponent<NativeBannerProps>(NATIVE_NAME)
48
+ })()
49
+
50
+ // ─── Public component ────────────────────────────────────────────────────────
51
+
19
52
  export interface SellwildBannerProps {
20
53
  config: SellwildConfig
21
54
  size: AdSize
@@ -35,40 +68,73 @@ export function SellwildBanner({
35
68
  onClick,
36
69
  onError,
37
70
  }: SellwildBannerProps) {
38
- const dimensions = AD_DIMENSIONS[size]
39
- const html = buildBannerHtml(config, zoneId, size)
71
+ const dim = AD_DIMENSIONS[size]
72
+ const containerStyle: ViewStyle = { width: dim.width, height: dim.height }
40
73
 
41
- const handleMessage = useCallback(
42
- (event: WebViewMessageEvent) => {
43
- try {
44
- const msg = JSON.parse(event.nativeEvent.data)
45
- if (msg.type === 'AD_IMPRESSION') onImpression?.()
46
- if (msg.type === 'AD_CLICK') onClick?.()
47
- } catch {
48
- // ignore
49
- }
50
- },
51
- [onImpression, onClick]
52
- )
74
+ if (!NativeBanner) {
75
+ // Native module not registered. Most common cause: iOS bridge not yet
76
+ // wired (1.3.0 ships the Android bridge first; iOS lands in 1.3.x).
77
+ // Render a visible placeholder in dev builds so the gap is obvious;
78
+ // production builds get a transparent slot of the right dimensions.
79
+ return (
80
+ <View style={[containerStyle, style, __DEV__ ? styles.devPlaceholder : undefined]}>
81
+ {__DEV__ ? (
82
+ <Text style={styles.devText}>
83
+ Sellwild native banner not available on {Platform.OS} (yet)
84
+ </Text>
85
+ ) : null}
86
+ </View>
87
+ )
88
+ }
89
+
90
+ // Pass only the fields the native banner path actually reads. The rest
91
+ // of SellwildConfig is ignored on the native side. Note: TS core uses
92
+ // `adRefreshInterval` (millis); the native side reads it as
93
+ // `adRefreshIntervalMs`. The bridge translates.
94
+ const nativeConfig = {
95
+ partnerCode: config.partnerCode,
96
+ appBundleId: config.appBundleId,
97
+ appStoreUrl: config.appStoreUrl,
98
+ gamTag: config.gamTag,
99
+ debug: config.debug,
100
+ adRefreshMax: config.adRefreshMax,
101
+ adRefreshMaxMobile: config.adRefreshMaxMobile,
102
+ adRefreshIntervalMs: config.adRefreshInterval,
103
+ prebidServer: config.prebidServer,
104
+ remote: config.remote,
105
+ }
53
106
 
54
107
  return (
55
- <View style={[{ width: dimensions.width, height: dimensions.height }, style]}>
56
- <WebView
57
- source={{ html, baseUrl: WIDGET_BASE_URL }}
58
- style={StyleSheet.absoluteFill}
59
- onMessage={handleMessage}
60
- javaScriptEnabled
61
- domStorageEnabled
62
- thirdPartyCookiesEnabled
63
- scrollEnabled={false}
64
- showsVerticalScrollIndicator={false}
65
- showsHorizontalScrollIndicator={false}
66
- mixedContentMode="compatibility"
67
- originWhitelist={['*']}
68
- onError={syntheticEvent => {
69
- onError?.(new Error(syntheticEvent.nativeEvent.description))
70
- }}
71
- />
72
- </View>
108
+ <NativeBanner
109
+ style={[containerStyle, style]}
110
+ config={nativeConfig}
111
+ size={size}
112
+ zoneId={String(zoneId)}
113
+ onAdLoaded={() => {
114
+ // No-op event hook today; surfaced for future fill metrics.
115
+ }}
116
+ onAdImpression={() => onImpression?.()}
117
+ onAdClicked={() => onClick?.()}
118
+ onAdFailed={(e: NativeSyntheticEvent<{ message: string }>) => {
119
+ const msg = e.nativeEvent?.message ?? 'Ad failed'
120
+ onError?.(new Error(msg))
121
+ }}
122
+ />
73
123
  )
74
124
  }
125
+
126
+ const styles = StyleSheet.create({
127
+ devPlaceholder: {
128
+ backgroundColor: '#FEE2E2',
129
+ borderWidth: 1,
130
+ borderColor: '#FCA5A5',
131
+ alignItems: 'center',
132
+ justifyContent: 'center',
133
+ padding: 4,
134
+ },
135
+ devText: {
136
+ color: '#991B1B',
137
+ fontSize: 11,
138
+ textAlign: 'center',
139
+ },
140
+ })
@@ -183,7 +183,7 @@ function configToAttributes(config: SellwildConfig): string {
183
183
  add('consent-management', config.consentManagement)
184
184
  add('schain-sid', config.schainSid)
185
185
  add('s2s-config', config.s2sConfig)
186
- add('iab-cats', config.iabCats?.join(','))
186
+ add('iab-cats', Array.isArray(config.iabCats) ? config.iabCats.join(',') : config.iabCats)
187
187
 
188
188
  // Third-party (typed fields with defaults — kept emitted explicitly for
189
189
  // backwards compatibility with static buildConfig() callers)