adwhale-sdk-react-native 2.7.204 → 2.7.300

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 (108) hide show
  1. package/AdwhaleSdkReactNative.podspec +22 -0
  2. package/README.md +80 -37
  3. package/android/proguard-rules.pro +56 -0
  4. package/android/src/main/java/com/adwhalesdkreactnative/AdWhaleCustomNativeBinderFactory.java +18 -0
  5. package/android/src/main/java/com/adwhalesdkreactnative/AdwhaleSdkReactNativePackage.java +51 -6
  6. package/android/src/main/java/com/adwhalesdkreactnative/BinderFactory.java +9 -0
  7. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdSettingModule.java +148 -49
  8. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdView.java +46 -27
  9. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAppOpenAd.java +136 -0
  10. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationCustomNativeAdView.java +37 -46
  11. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationExitPopupAd.java +240 -0
  12. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationInterstitialAd.java +189 -0
  13. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationLoggerModule.java +2 -1
  14. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationNativeAdViewListenerBridge.java +83 -0
  15. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationRewardAd.java +255 -6
  16. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTemplateNativeAdView.java +191 -70
  17. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTransitionPopupAd.java +218 -0
  18. package/android/src/main/java/com/adwhalesdkreactnative/RNWrapperView.java +137 -0
  19. package/android/src/main/java/com/adwhalesdkreactnative/SimpleBinderFactory.java +1 -1
  20. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodArgConst.java +112 -0
  21. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodCallConst.java +113 -0
  22. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodResultConst.java +28 -0
  23. package/build/generated/ios/ReactAppDependencyProvider.podspec +34 -0
  24. package/ios/AdWhaleNativeCustomViewFactoryRegistry.swift +56 -0
  25. package/ios/AdwhaleSdkReactNative.mm +96 -5
  26. package/ios/AdwhaleSdkReactNativeModule.swift +148 -0
  27. package/ios/RNAdWhaleMediationAdViewManager.m +38 -0
  28. package/ios/RNAdWhaleMediationAdViewManager.swift +14 -0
  29. package/ios/RNAdWhaleMediationAppOpenAd.m +18 -0
  30. package/ios/RNAdWhaleMediationAppOpenAd.swift +175 -0
  31. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.m +22 -0
  32. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.swift +239 -0
  33. package/ios/RNAdWhaleMediationInterstitialAd.m +18 -0
  34. package/ios/RNAdWhaleMediationInterstitialAd.swift +161 -0
  35. package/ios/RNAdWhaleMediationRewardAd.m +18 -0
  36. package/ios/RNAdWhaleMediationRewardAd.swift +172 -0
  37. package/ios/WhaleMediationBannerContainer.swift +182 -0
  38. package/lib/module/AdWhaleAdView.js +58 -3
  39. package/lib/module/AdWhaleAdView.js.map +1 -1
  40. package/lib/module/AdWhaleAppOpenAd.js +223 -25
  41. package/lib/module/AdWhaleAppOpenAd.js.map +1 -1
  42. package/lib/module/AdWhaleExitPopupAd.js +93 -0
  43. package/lib/module/AdWhaleExitPopupAd.js.map +1 -0
  44. package/lib/module/AdWhaleInterstitialAd.js +146 -22
  45. package/lib/module/AdWhaleInterstitialAd.js.map +1 -1
  46. package/lib/module/AdWhaleMediationAds.js +75 -4
  47. package/lib/module/AdWhaleMediationAds.js.map +1 -1
  48. package/lib/module/AdWhaleNativeCustomView.js +59 -8
  49. package/lib/module/AdWhaleNativeCustomView.js.map +1 -1
  50. package/lib/module/AdWhaleNativeTemplateView.js +37 -19
  51. package/lib/module/AdWhaleNativeTemplateView.js.map +1 -1
  52. package/lib/module/AdWhaleRewardAd.js +169 -24
  53. package/lib/module/AdWhaleRewardAd.js.map +1 -1
  54. package/lib/module/AdWhaleTransitionPopupAd.js +87 -0
  55. package/lib/module/AdWhaleTransitionPopupAd.js.map +1 -0
  56. package/lib/module/NativeAdwhaleSdkReactNative.js +16 -1
  57. package/lib/module/NativeAdwhaleSdkReactNative.js.map +1 -1
  58. package/lib/module/constants/AdWhaleMethodArgConst.js +53 -0
  59. package/lib/module/constants/AdWhaleMethodArgConst.js.map +1 -0
  60. package/lib/module/constants/AdWhaleMethodCallConst.js +56 -0
  61. package/lib/module/constants/AdWhaleMethodCallConst.js.map +1 -0
  62. package/lib/module/constants/AdWhaleMethodResultConst.js +16 -0
  63. package/lib/module/constants/AdWhaleMethodResultConst.js.map +1 -0
  64. package/lib/module/index.js +10 -1
  65. package/lib/module/index.js.map +1 -1
  66. package/lib/typescript/src/AdWhaleAdView.d.ts +20 -2
  67. package/lib/typescript/src/AdWhaleAdView.d.ts.map +1 -1
  68. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts +10 -0
  69. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts.map +1 -1
  70. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts +36 -0
  71. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts.map +1 -0
  72. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts +16 -0
  73. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts.map +1 -1
  74. package/lib/typescript/src/AdWhaleMediationAds.d.ts +13 -5
  75. package/lib/typescript/src/AdWhaleMediationAds.d.ts.map +1 -1
  76. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts +7 -4
  77. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts.map +1 -1
  78. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts +20 -7
  79. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts.map +1 -1
  80. package/lib/typescript/src/AdWhaleRewardAd.d.ts +18 -2
  81. package/lib/typescript/src/AdWhaleRewardAd.d.ts.map +1 -1
  82. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts +33 -0
  83. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts.map +1 -0
  84. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts +28 -6
  85. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts.map +1 -1
  86. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts +51 -0
  87. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts.map +1 -0
  88. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts +53 -0
  89. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts.map +1 -0
  90. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts +14 -0
  91. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts.map +1 -0
  92. package/lib/typescript/src/index.d.ts +9 -1
  93. package/lib/typescript/src/index.d.ts.map +1 -1
  94. package/package.json +1 -1
  95. package/src/AdWhaleAdView.tsx +92 -4
  96. package/src/AdWhaleAppOpenAd.ts +293 -38
  97. package/src/AdWhaleExitPopupAd.ts +183 -0
  98. package/src/AdWhaleInterstitialAd.ts +206 -36
  99. package/src/AdWhaleMediationAds.ts +108 -4
  100. package/src/AdWhaleNativeCustomView.tsx +85 -23
  101. package/src/AdWhaleNativeTemplateView.tsx +79 -42
  102. package/src/AdWhaleRewardAd.ts +245 -43
  103. package/src/AdWhaleTransitionPopupAd.ts +171 -0
  104. package/src/NativeAdwhaleSdkReactNative.ts +30 -6
  105. package/src/constants/AdWhaleMethodArgConst.ts +50 -0
  106. package/src/constants/AdWhaleMethodCallConst.ts +60 -0
  107. package/src/constants/AdWhaleMethodResultConst.ts +13 -0
  108. package/src/index.ts +33 -0
@@ -22,22 +22,37 @@ export interface AdWhaleInterstitialGcoder {
22
22
  }
23
23
 
24
24
  interface RNAdWhaleMediationInterstitialAdNativeModule {
25
- setPlacementName(placementName: string): void;
26
- setRegion(region: string): void;
27
- setGcoder(gcoder: { lt: number; lng: number }): void;
28
- loadAd(placementUid: string): void;
29
- showAd(): void;
25
+ setPlacementName?(placementName: string): void;
26
+ setRegion?(region: string): void;
27
+ setGcoder?(gcoder: { lt: number; lng: number }): void;
28
+ loadAd?(placementUid: string): void;
29
+ showAd?(): void;
30
+ /** Flutter와 동일 네이밍: adId 기반 로드 */
31
+ loadInterstitialAd?(args: {
32
+ adId: number;
33
+ placementUid: string;
34
+ placementName?: string;
35
+ region?: string;
36
+ gcoder?: [number, number];
37
+ }): void;
38
+ /** Flutter와 동일 네이밍: adId 기반 show */
39
+ showAdWithoutView?(adId: number): void;
40
+ /** Flutter와 동일 네이밍: adId 기반 destroy */
41
+ destroyAd?(adId: number): void;
30
42
  addListener(eventName: string): void;
31
43
  removeListeners(count: number): void;
32
44
  }
33
45
 
46
+ const LINKING_ERROR =
47
+ 'RNAdWhaleMediationInterstitialAd native module is not linked on this platform.';
48
+
34
49
  const { RNAdWhaleMediationInterstitialAd } = NativeModules as {
35
- RNAdWhaleMediationInterstitialAd: RNAdWhaleMediationInterstitialAdNativeModule;
50
+ RNAdWhaleMediationInterstitialAd?: RNAdWhaleMediationInterstitialAdNativeModule;
36
51
  };
37
52
 
38
- const interstitialEmitter = new NativeEventEmitter(
39
- RNAdWhaleMediationInterstitialAd,
40
- );
53
+ const interstitialEmitter = RNAdWhaleMediationInterstitialAd
54
+ ? new NativeEventEmitter(RNAdWhaleMediationInterstitialAd)
55
+ : null;
41
56
 
42
57
  export const AdWhaleInterstitialAd = {
43
58
  loadAd(
@@ -48,34 +63,124 @@ export const AdWhaleInterstitialAd = {
48
63
  gcoder?: AdWhaleInterstitialGcoder;
49
64
  },
50
65
  ) {
66
+ if (!RNAdWhaleMediationInterstitialAd) {
67
+ console.warn(LINKING_ERROR);
68
+ return;
69
+ }
51
70
  // 먼저 옵션들을 설정
52
71
  if (options?.placementName) {
53
- RNAdWhaleMediationInterstitialAd.setPlacementName(options.placementName);
72
+ RNAdWhaleMediationInterstitialAd.setPlacementName?.(options.placementName);
54
73
  }
55
74
  if (options?.region) {
56
- RNAdWhaleMediationInterstitialAd.setRegion(options.region);
75
+ RNAdWhaleMediationInterstitialAd.setRegion?.(options.region);
57
76
  }
58
77
  if (options?.gcoder) {
59
- RNAdWhaleMediationInterstitialAd.setGcoder(options.gcoder);
78
+ RNAdWhaleMediationInterstitialAd.setGcoder?.(options.gcoder);
79
+ }
80
+ // 네이티브 구현 차이 대응: loadAd 우선, 없으면 adId 기반 API로 폴백
81
+ if (RNAdWhaleMediationInterstitialAd.loadAd) {
82
+ RNAdWhaleMediationInterstitialAd.loadAd(placementUid);
83
+ return;
60
84
  }
61
- // 다음 loadAd 호출
62
- RNAdWhaleMediationInterstitialAd.loadAd(placementUid);
85
+ if (RNAdWhaleMediationInterstitialAd.loadInterstitialAd) {
86
+ RNAdWhaleMediationInterstitialAd.loadInterstitialAd({
87
+ adId: 0,
88
+ placementUid,
89
+ placementName: options?.placementName,
90
+ region: options?.region,
91
+ gcoder: options?.gcoder
92
+ ? [options.gcoder.lt, options.gcoder.lng]
93
+ : undefined,
94
+ });
95
+ return;
96
+ }
97
+ console.warn(
98
+ '[AdWhaleInterstitialAd] Native module missing both loadAd and loadInterstitialAd.',
99
+ );
63
100
  },
64
101
 
65
102
  showAd() {
66
- RNAdWhaleMediationInterstitialAd.showAd();
103
+ if (!RNAdWhaleMediationInterstitialAd) {
104
+ console.warn(LINKING_ERROR);
105
+ return;
106
+ }
107
+ // 네이티브 구현 차이 대응: showAd 우선, 없으면 adId 기반 API로 폴백
108
+ if (RNAdWhaleMediationInterstitialAd.showAd) {
109
+ RNAdWhaleMediationInterstitialAd.showAd();
110
+ return;
111
+ }
112
+ if (RNAdWhaleMediationInterstitialAd.showAdWithoutView) {
113
+ RNAdWhaleMediationInterstitialAd.showAdWithoutView(0);
114
+ return;
115
+ }
116
+ console.warn(
117
+ '[AdWhaleInterstitialAd] Native module missing both showAd and showAdWithoutView.',
118
+ );
119
+ },
120
+
121
+ /**
122
+ * Flutter와 동일한 형태로 맞춘 신규 API (권장).
123
+ * - 광고를 여러 개 동시에 운용하려면 adId를 사용하세요.
124
+ */
125
+ loadInterstitialAd(
126
+ adId: number,
127
+ placementUid: string,
128
+ options?: {
129
+ placementName?: string;
130
+ region?: string;
131
+ gcoder?: AdWhaleInterstitialGcoder;
132
+ },
133
+ ) {
134
+ if (!RNAdWhaleMediationInterstitialAd) {
135
+ console.warn(LINKING_ERROR);
136
+ return;
137
+ }
138
+ if (!RNAdWhaleMediationInterstitialAd.loadInterstitialAd) {
139
+ // 네이티브가 아직 업데이트되지 않은 경우: 기존 싱글 인스턴스 경로로 폴백
140
+ this.loadAd(placementUid, options);
141
+ return;
142
+ }
143
+ RNAdWhaleMediationInterstitialAd.loadInterstitialAd({
144
+ adId,
145
+ placementUid,
146
+ placementName: options?.placementName,
147
+ region: options?.region,
148
+ gcoder: options?.gcoder ? [options.gcoder.lt, options.gcoder.lng] : undefined,
149
+ });
150
+ },
151
+
152
+ showAdWithoutView(adId: number) {
153
+ if (!RNAdWhaleMediationInterstitialAd) {
154
+ console.warn(LINKING_ERROR);
155
+ return;
156
+ }
157
+ if (!RNAdWhaleMediationInterstitialAd.showAdWithoutView) {
158
+ // 폴백: 싱글 인스턴스 show
159
+ this.showAd();
160
+ return;
161
+ }
162
+ RNAdWhaleMediationInterstitialAd.showAdWithoutView(adId);
163
+ },
164
+
165
+ destroyAd(adId: number) {
166
+ RNAdWhaleMediationInterstitialAd?.destroyAd?.(adId);
67
167
  },
68
168
 
69
169
  addEventListeners(listeners: AdWhaleInterstitialEvents): EmitterSubscription[] {
170
+ if (!interstitialEmitter) {
171
+ console.warn(LINKING_ERROR);
172
+ return [];
173
+ }
70
174
  const subs: EmitterSubscription[] = [];
175
+ let directShowedReceived = false;
71
176
 
72
- if (listeners.onLoaded) {
73
- subs.push(
74
- interstitialEmitter.addListener('onInterstitialAdLoaded', () => {
75
- listeners.onLoaded?.();
76
- }),
77
- );
78
- }
177
+ subs.push(
178
+ interstitialEmitter.addListener('onInterstitialAdLoaded', () => {
179
+ directShowedReceived = false;
180
+ console.log('AdWhaleInterstitialAd.ts Interstitial onLoaded');
181
+ listeners.onLoaded?.();
182
+ }),
183
+ );
79
184
  if (listeners.onLoadFailed) {
80
185
  subs.push(
81
186
  interstitialEmitter.addListener(
@@ -84,13 +189,13 @@ export const AdWhaleInterstitialAd = {
84
189
  ),
85
190
  );
86
191
  }
87
- if (listeners.onShowed) {
88
- subs.push(
89
- interstitialEmitter.addListener('onInterstitialAdShowed', () => {
90
- listeners.onShowed?.();
91
- }),
92
- );
93
- }
192
+ subs.push(
193
+ interstitialEmitter.addListener('onInterstitialAdShowed', () => {
194
+ directShowedReceived = true;
195
+ console.log('AdWhaleInterstitialAd.ts Interstitial onAdShowed');
196
+ listeners.onShowed?.();
197
+ }),
198
+ );
94
199
  if (listeners.onShowFailed) {
95
200
  subs.push(
96
201
  interstitialEmitter.addListener(
@@ -99,13 +204,23 @@ export const AdWhaleInterstitialAd = {
99
204
  ),
100
205
  );
101
206
  }
102
- if (listeners.onClosed) {
103
- subs.push(
104
- interstitialEmitter.addListener('onInterstitialAdClosed', () => {
105
- listeners.onClosed?.();
106
- }),
107
- );
108
- }
207
+ subs.push(
208
+ interstitialEmitter.addListener('onInterstitialAdClosed', () => {
209
+ directShowedReceived = false;
210
+ console.log('AdWhaleInterstitialAd.ts Interstitial onAdClosed');
211
+ listeners.onClosed?.();
212
+ }),
213
+ );
214
+ subs.push(
215
+ interstitialEmitter.addListener('onAdEvent', (payload: any) => {
216
+ if (payload?.eventName !== 'onInterstitialAdShowed' || directShowedReceived) {
217
+ return;
218
+ }
219
+ directShowedReceived = true;
220
+ console.log('AdWhaleInterstitialAd.ts Interstitial onAdShowed');
221
+ listeners.onShowed?.();
222
+ }),
223
+ );
109
224
  if (listeners.onClicked) {
110
225
  subs.push(
111
226
  interstitialEmitter.addListener('onInterstitialAdClicked', () => {
@@ -116,4 +231,59 @@ export const AdWhaleInterstitialAd = {
116
231
 
117
232
  return subs;
118
233
  },
234
+
235
+ /**
236
+ * Flutter `onAdEvent` 스타일: payload에 eventName + adId를 포함.
237
+ * 네이티브가 지원하는 경우, 특정 adId만 필터링해서 이벤트를 받을 수 있습니다.
238
+ */
239
+ addAdEventListenersById(
240
+ adId: number,
241
+ listeners: AdWhaleInterstitialEvents,
242
+ ): EmitterSubscription[] {
243
+ if (!interstitialEmitter) {
244
+ console.warn(LINKING_ERROR);
245
+ return [];
246
+ }
247
+ const subs: EmitterSubscription[] = [];
248
+
249
+ const handle = (eventName: string, cb?: (e?: any) => void) => {
250
+ if (!cb) return;
251
+ subs.push(
252
+ interstitialEmitter.addListener('onAdEvent', (payload: any) => {
253
+ if (!payload || payload.adId !== adId || payload.eventName !== eventName) {
254
+ return;
255
+ }
256
+ cb(payload);
257
+ }),
258
+ );
259
+ };
260
+
261
+ handle('onInterstitialAdLoaded', () => {
262
+ console.log('AdWhaleInterstitialAd.ts Interstitial onLoaded');
263
+ listeners.onLoaded?.();
264
+ });
265
+ handle('onInterstitialAdLoadFailed', p =>
266
+ listeners.onLoadFailed?.({
267
+ statusCode: Number(p?.statusCode ?? -1),
268
+ message: String(p?.message ?? ''),
269
+ }),
270
+ );
271
+ handle('onInterstitialAdShowed', () => {
272
+ console.log('AdWhaleInterstitialAd.ts Interstitial onAdShowed');
273
+ listeners.onShowed?.();
274
+ });
275
+ handle('onInterstitialAdShowFailed', p =>
276
+ listeners.onShowFailed?.({
277
+ statusCode: Number(p?.statusCode ?? -1),
278
+ message: String(p?.message ?? ''),
279
+ }),
280
+ );
281
+ handle('onInterstitialAdClosed', () => {
282
+ console.log('AdWhaleInterstitialAd.ts Interstitial onAdClosed');
283
+ listeners.onClosed?.();
284
+ });
285
+ handle('onInterstitialAdClicked', () => listeners.onClicked?.());
286
+
287
+ return subs;
288
+ },
119
289
  };
@@ -1,6 +1,10 @@
1
1
  // src/AdWhaleMediationAds.ts
2
2
  import { NativeModules, Platform } from 'react-native';
3
- import NativeAdwhaleSdkReactNative, { type InitializeResult } from './NativeAdwhaleSdkReactNative';
3
+ import NativeAdwhaleSdkReactNative, {
4
+ type AdInspectorResult,
5
+ type GdprConsentResult,
6
+ type InitializeResult,
7
+ } from './NativeAdwhaleSdkReactNative';
4
8
 
5
9
  const LINKING_ERROR =
6
10
  `AdWhale Mediation SDK native modules not linked.\n` +
@@ -10,6 +14,9 @@ const LINKING_ERROR =
10
14
  ` • Web 환경에서 실행 중이 아닌지\n` +
11
15
  `Platform: ${Platform.OS}`;
12
16
 
17
+ const IOS_UNSUPPORTED_LOGGER =
18
+ '[AdWhaleMediationAds] setLoggerEnabled: iOS에서는 RN 로거 모듈(RNAdWhaleMediationLoggerModule)을 지원하지 않습니다.';
19
+
13
20
  interface RNAdWhaleMediationLoggerModule {
14
21
  setLogLevel?(enabled: boolean): void;
15
22
  getLogLevel?(): Promise<string>;
@@ -19,7 +26,6 @@ const { RNAdWhaleMediationLoggerModule } = NativeModules as {
19
26
  RNAdWhaleMediationLoggerModule?: RNAdWhaleMediationLoggerModule;
20
27
  };
21
28
 
22
- // TurboModule spec을 통해 접근
23
29
  const AdwhaleSdkReactNative = NativeAdwhaleSdkReactNative;
24
30
 
25
31
  if (!AdwhaleSdkReactNative) {
@@ -32,22 +38,46 @@ if (!AdwhaleSdkReactNative) {
32
38
  hasResetGdprConsentStatus: !!AdwhaleSdkReactNative.resetGdprConsentStatus,
33
39
  hasSetGdpr: !!AdwhaleSdkReactNative.setGdpr,
34
40
  hasSetCoppa: !!AdwhaleSdkReactNative.setCoppa,
41
+ hasSetTagForUnderAgeOfConsent:
42
+ !!AdwhaleSdkReactNative.setTagForUnderAgeOfConsent,
43
+ hasSetMaxAdContentRating: !!AdwhaleSdkReactNative.setMaxAdContentRating,
44
+ hasSetTestDeviceIdentifiers:
45
+ !!AdwhaleSdkReactNative.setTestDeviceIdentifiers,
46
+ hasShowAdInspector: !!AdwhaleSdkReactNative.showAdInspector,
47
+ hasSetAppMuted: !!AdwhaleSdkReactNative.setAppMuted,
48
+ hasSetAppVolume: !!AdwhaleSdkReactNative.setAppVolume,
35
49
  });
36
50
  }
37
51
 
38
52
  export const AdWhaleMediationAds = {
53
+ /**
54
+ * Flutter `AdWhaleMediationAds.instance.initialize()` 와 동일.
55
+ * 네이티브 TurboModule 은 codegen 에 따라 `initialize` 를 호출하며, 성공 맵은 `statusCode` + `message` 만 반환합니다.
56
+ */
39
57
  initialize(): Promise<InitializeResult> {
40
58
  if (!AdwhaleSdkReactNative?.initialize) {
41
59
  return Promise.reject(new Error(LINKING_ERROR));
42
60
  }
43
- return AdwhaleSdkReactNative.initialize();
61
+ return AdwhaleSdkReactNative.initialize().catch((e: unknown) => ({
62
+ statusCode: -1,
63
+ message: e instanceof Error ? e.message : String(e),
64
+ }));
44
65
  },
45
66
 
46
67
  setLoggerEnabled(enabled: boolean) {
68
+ if (Platform.OS === 'ios') {
69
+ console.warn(IOS_UNSUPPORTED_LOGGER);
70
+ return;
71
+ }
47
72
  RNAdWhaleMediationLoggerModule?.setLogLevel?.(enabled);
48
73
  },
49
74
 
50
75
  getLogLevel(): Promise<string> {
76
+ if (Platform.OS === 'ios') {
77
+ return Promise.reject(
78
+ new Error('getLogLevel is not supported on iOS (Android 전용).')
79
+ );
80
+ }
51
81
  if (!RNAdWhaleMediationLoggerModule?.getLogLevel) {
52
82
  return Promise.reject(
53
83
  new Error('getLogLevel is not implemented in native module')
@@ -65,7 +95,7 @@ export const AdWhaleMediationAds = {
65
95
  AdwhaleSdkReactNative.setCoppa(enabled);
66
96
  },
67
97
 
68
- requestGdprConsent(): Promise<{ isSuccess: boolean; message: string }> {
98
+ requestGdprConsent(): Promise<GdprConsentResult> {
69
99
  console.log('[AdWhaleMediationAds] requestGdprConsent called');
70
100
  if (!AdwhaleSdkReactNative?.requestGdprConsent) {
71
101
  const error = new Error('requestGdprConsent is not implemented in native module');
@@ -100,10 +130,84 @@ export const AdWhaleMediationAds = {
100
130
 
101
131
  setGdpr(consent: boolean) {
102
132
  console.log('[AdWhaleMediationAds] setGdpr called:', consent);
133
+ if (Platform.OS === 'ios') {
134
+ console.warn(
135
+ '[AdWhaleMediationAds] setGdpr: iOS AdWhale SDK 에 대응 API가 없어 지원하지 않습니다 (Android 전용).'
136
+ );
137
+ return;
138
+ }
103
139
  if (!AdwhaleSdkReactNative?.setGdpr) {
104
140
  console.warn('[AdWhaleMediationAds] setGdpr is not implemented in native module');
105
141
  return;
106
142
  }
107
143
  AdwhaleSdkReactNative.setGdpr(consent);
108
144
  },
145
+
146
+ setTagForUnderAgeOfConsent(tag: boolean) {
147
+ if (!AdwhaleSdkReactNative?.setTagForUnderAgeOfConsent) {
148
+ console.warn(
149
+ '[AdWhaleMediationAds] setTagForUnderAgeOfConsent is not implemented in native module'
150
+ );
151
+ return;
152
+ }
153
+ AdwhaleSdkReactNative.setTagForUnderAgeOfConsent(tag);
154
+ },
155
+
156
+ setMaxAdContentRating(rating: string | null | undefined) {
157
+ if (!AdwhaleSdkReactNative?.setMaxAdContentRating) {
158
+ console.warn(
159
+ '[AdWhaleMediationAds] setMaxAdContentRating is not implemented in native module'
160
+ );
161
+ return;
162
+ }
163
+ if (rating === undefined || rating === '') {
164
+ AdwhaleSdkReactNative.setMaxAdContentRating(null);
165
+ } else {
166
+ AdwhaleSdkReactNative.setMaxAdContentRating(rating);
167
+ }
168
+ },
169
+
170
+ setTestDeviceIdentifiers(testDeviceIds: string[] | null | undefined) {
171
+ if (testDeviceIds == null) {
172
+ return;
173
+ }
174
+ if (!AdwhaleSdkReactNative?.setTestDeviceIdentifiers) {
175
+ console.warn(
176
+ '[AdWhaleMediationAds] setTestDeviceIdentifiers is not implemented in native module'
177
+ );
178
+ return;
179
+ }
180
+ AdwhaleSdkReactNative.setTestDeviceIdentifiers(testDeviceIds);
181
+ },
182
+
183
+ showAdInspector(): Promise<AdInspectorResult> {
184
+ if (!AdwhaleSdkReactNative?.showAdInspector) {
185
+ return Promise.reject(
186
+ new Error('showAdInspector is not implemented in native module')
187
+ );
188
+ }
189
+ return AdwhaleSdkReactNative.showAdInspector();
190
+ },
191
+
192
+ setAppMuted(isMuted: boolean) {
193
+ if (!AdwhaleSdkReactNative?.setAppMuted) {
194
+ console.warn(
195
+ '[AdWhaleMediationAds] setAppMuted is not implemented in native module'
196
+ );
197
+ return;
198
+ }
199
+ AdwhaleSdkReactNative.setAppMuted(isMuted);
200
+ },
201
+
202
+ setAppVolume(volume: number) {
203
+ if (!AdwhaleSdkReactNative?.setAppVolume) {
204
+ console.warn(
205
+ '[AdWhaleMediationAds] setAppVolume is not implemented in native module'
206
+ );
207
+ return;
208
+ }
209
+ AdwhaleSdkReactNative.setAppVolume(volume);
210
+ },
109
211
  };
212
+
213
+ export type { InitializeResult, GdprConsentResult, AdInspectorResult };
@@ -8,8 +8,8 @@ import type {
8
8
  } from 'react-native';
9
9
 
10
10
  export interface AdWhaleNativeCustomError {
11
- errorCode: number;
12
- errorMessage: string;
11
+ statusCode: number;
12
+ message: string;
13
13
  }
14
14
 
15
15
  export interface AdWhaleNativeCustomGcoder {
@@ -25,8 +25,11 @@ export interface AdWhaleNativeCustomViewProps {
25
25
  region?: string;
26
26
  gcoder?: AdWhaleNativeCustomGcoder;
27
27
 
28
- onAdLoaded?: () => void;
29
- onAdFailedToLoad?: (event: AdWhaleNativeCustomError) => void;
28
+ onNativeAdLoaded?: () => void;
29
+ onNativeAdFailedToLoad?: (event: AdWhaleNativeCustomError) => void;
30
+ onNativeAdShowFailed?: (event: AdWhaleNativeCustomError) => void;
31
+ onNativeAdClicked?: () => void;
32
+ onNativeAdClosed?: () => void;
30
33
  }
31
34
 
32
35
  export interface AdWhaleNativeCustomHandle {
@@ -37,11 +40,16 @@ export interface AdWhaleNativeCustomHandle {
37
40
  type NativeErrorEvent = NativeSyntheticEvent<AdWhaleNativeCustomError>;
38
41
 
39
42
  interface NativeProps
40
- extends Omit<AdWhaleNativeCustomViewProps, 'onAdFailedToLoad'> {
41
- onAdFailedToLoad?: (e: NativeErrorEvent) => void;
43
+ extends Omit<
44
+ AdWhaleNativeCustomViewProps,
45
+ 'onNativeAdFailedToLoad' | 'onNativeAdShowFailed'
46
+ > {
47
+ onNativeAdFailedToLoad?: (e: NativeErrorEvent) => void;
48
+ onNativeAdShowFailed?: (e: NativeErrorEvent) => void;
42
49
  }
43
50
 
44
51
  const VIEW_MANAGER_NAME = 'RNAdWhaleMediationCustomNativeAdView';
52
+ const LOG_PREFIX = '[adwhale_sdk_react_native] AdWhaleNativeCustomView.tsx';
45
53
 
46
54
  const NativeComponent =
47
55
  requireNativeComponent<NativeProps>(VIEW_MANAGER_NAME);
@@ -55,41 +63,95 @@ export const AdWhaleNativeCustomView = forwardRef<
55
63
  useImperativeHandle(ref, () => ({
56
64
  loadAd() {
57
65
  const handle = findNodeHandle(innerRef.current);
58
- if (!handle) return;
66
+ console.log(`${LOG_PREFIX} loadAd() called handle=${String(handle)}`);
67
+ if (!handle) {
68
+ console.log(`${LOG_PREFIX} loadAd() skipped: handle is null`);
69
+ return;
70
+ }
59
71
  const config = UIManager.getViewManagerConfig(VIEW_MANAGER_NAME) as
60
72
  | { Commands?: { loadAd?: number; showAd?: number } }
61
73
  | undefined;
62
- if (!config?.Commands?.loadAd) return;
63
- UIManager.dispatchViewManagerCommand(
64
- handle,
65
- config.Commands.loadAd,
66
- [],
67
- );
74
+ const loadCommand = config?.Commands?.loadAd;
75
+ if (loadCommand != null) {
76
+ console.log(`${LOG_PREFIX} dispatch loadAd commandId=${String(loadCommand)}`);
77
+ UIManager.dispatchViewManagerCommand(handle, loadCommand, []);
78
+ return;
79
+ }
80
+ console.log(`${LOG_PREFIX} loadAd commandId missing, fallback to string command`);
81
+ try {
82
+ (UIManager as any).dispatchViewManagerCommand(handle, 'loadAd', []);
83
+ } catch (error) {
84
+ console.log(
85
+ `${LOG_PREFIX} loadAd fallback dispatch failed: ${String(error)}`,
86
+ );
87
+ }
68
88
  },
69
89
  showAd() {
70
90
  const handle = findNodeHandle(innerRef.current);
71
- if (!handle) return;
91
+ console.log(`${LOG_PREFIX} showAd() called handle=${String(handle)}`);
92
+ if (!handle) {
93
+ console.log(`${LOG_PREFIX} showAd() skipped: handle is null`);
94
+ return;
95
+ }
72
96
  const config = UIManager.getViewManagerConfig(VIEW_MANAGER_NAME) as
73
97
  | { Commands?: { loadAd?: number; showAd?: number } }
74
98
  | undefined;
75
- if (!config?.Commands?.showAd) return;
76
- UIManager.dispatchViewManagerCommand(
77
- handle,
78
- config.Commands.showAd,
79
- [],
80
- );
99
+ const showCommand = config?.Commands?.showAd;
100
+ if (showCommand != null) {
101
+ console.log(`${LOG_PREFIX} dispatch showAd commandId=${String(showCommand)}`);
102
+ UIManager.dispatchViewManagerCommand(handle, showCommand, []);
103
+ return;
104
+ }
105
+ console.log(`${LOG_PREFIX} showAd commandId missing, fallback to string command`);
106
+ try {
107
+ (UIManager as any).dispatchViewManagerCommand(handle, 'showAd', []);
108
+ } catch (error) {
109
+ console.log(
110
+ `${LOG_PREFIX} showAd fallback dispatch failed: ${String(error)}`,
111
+ );
112
+ }
81
113
  },
82
114
  }));
83
115
 
84
- const handleFailedToLoad: NativeProps['onAdFailedToLoad'] = e => {
85
- props.onAdFailedToLoad?.(e.nativeEvent);
116
+ const handleFailedToLoad: NativeProps['onNativeAdFailedToLoad'] = e => {
117
+ const ev = e.nativeEvent;
118
+ queueMicrotask(() => props.onNativeAdFailedToLoad?.(ev));
119
+ };
120
+
121
+ const handleShowFailed: NativeProps['onNativeAdShowFailed'] = e => {
122
+ const ev = e.nativeEvent;
123
+ queueMicrotask(() => props.onNativeAdShowFailed?.(ev));
124
+ };
125
+
126
+ const handleLoaded = () => {
127
+ console.log('AdWhaleNativeCustomView.tsx CustomNative onLoaded');
128
+ // 네이티브 직접 이벤트가 레이아웃/커밋 중 동기 호출되면 setState 가 먹지 않는 경우가 있어 한 틱 미룸
129
+ queueMicrotask(() => props.onNativeAdLoaded?.());
130
+ };
131
+
132
+ const handleClicked = () => {
133
+ console.log('AdWhaleNativeCustomView.tsx CustomNative onClicked');
134
+ queueMicrotask(() => props.onNativeAdClicked?.());
135
+ };
136
+
137
+ const handleClosed = () => {
138
+ console.log('AdWhaleNativeCustomView.tsx CustomNative onClosed');
139
+ queueMicrotask(() => props.onNativeAdClosed?.());
86
140
  };
87
141
 
88
142
  return (
89
143
  <NativeComponent
90
144
  ref={innerRef}
91
145
  {...props}
92
- onAdFailedToLoad={props.onAdFailedToLoad ? handleFailedToLoad : undefined}
146
+ onNativeAdLoaded={handleLoaded}
147
+ onNativeAdFailedToLoad={
148
+ props.onNativeAdFailedToLoad ? handleFailedToLoad : undefined
149
+ }
150
+ onNativeAdShowFailed={
151
+ props.onNativeAdShowFailed ? handleShowFailed : undefined
152
+ }
153
+ onNativeAdClicked={handleClicked}
154
+ onNativeAdClosed={handleClosed}
93
155
  />
94
156
  );
95
157
  });