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
@@ -1,9 +1,10 @@
1
1
  // src/AdWhaleNativeTemplateView.tsx
2
- import React, { forwardRef, useImperativeHandle, useRef } from 'react';
3
- import { requireNativeComponent, UIManager, findNodeHandle } from 'react-native';
2
+ import { forwardRef, useImperativeHandle, useState } from 'react';
3
+ import { Platform, requireNativeComponent } from 'react-native';
4
4
  import type {
5
5
  NativeSyntheticEvent,
6
6
  StyleProp,
7
+ ViewProps,
7
8
  ViewStyle,
8
9
  } from 'react-native';
9
10
 
@@ -29,8 +30,8 @@ export interface AdWhaleNativeTemplateStyle {
29
30
  }
30
31
 
31
32
  export interface AdWhaleNativeTemplateError {
32
- errorCode: number;
33
- errorMessage: string;
33
+ statusCode: number;
34
+ message: string;
34
35
  }
35
36
 
36
37
  export interface AdWhaleNativeTemplateGcoder {
@@ -47,9 +48,24 @@ export interface AdWhaleNativeTemplateViewProps {
47
48
  template: AdWhaleNativeTemplateType;
48
49
  templateStyle?: AdWhaleNativeTemplateStyle;
49
50
 
50
- onAdLoaded?: () => void;
51
- onAdFailedToLoad?: (event: AdWhaleNativeTemplateError) => void;
52
- onAdShowFailed?: (event: AdWhaleNativeTemplateError) => void;
51
+ /**
52
+ * Android Fabric: `ref.loadAd()` 대신 부모 state로 증가시키는 방식(권장).
53
+ * 지정 0은 무시, 1 이상이 바뀔 때마다 네이티브 load. 타입 전환 시 0으로 리셋 후 증가.
54
+ */
55
+ loadSignal?: number;
56
+ /** {@link loadSignal}과 동일하게 표시 트리거 */
57
+ showSignal?: number;
58
+ /**
59
+ * 템플릿/스타일 전환 시마다 바꿔 주는 논리 인스턴스 키(예: `adViewKey`).
60
+ * `loadSignal`이 다시 1이 되어도 네이티브가 이전 인스턴스와 동일 요청으로 오인해 스킵하지 않게 합니다.
61
+ */
62
+ viewSyncKey?: number;
63
+
64
+ onNativeAdLoaded?: () => void;
65
+ onNativeAdFailedToLoad?: (event: AdWhaleNativeTemplateError) => void;
66
+ onNativeAdShowFailed?: (event: AdWhaleNativeTemplateError) => void;
67
+ onNativeAdClicked?: () => void;
68
+ onNativeAdClosed?: () => void;
53
69
  }
54
70
 
55
71
  export interface AdWhaleNativeTemplateHandle {
@@ -62,10 +78,19 @@ type NativeErrorEvent = NativeSyntheticEvent<AdWhaleNativeTemplateError>;
62
78
  interface NativeProps
63
79
  extends Omit<
64
80
  AdWhaleNativeTemplateViewProps,
65
- 'onAdFailedToLoad' | 'onAdShowFailed'
66
- > {
67
- onAdFailedToLoad?: (e: NativeErrorEvent) => void;
68
- onAdShowFailed?: (e: NativeErrorEvent) => void;
81
+ | 'onNativeAdFailedToLoad'
82
+ | 'onNativeAdShowFailed'
83
+ | 'loadSignal'
84
+ | 'showSignal'
85
+ | 'viewSyncKey'
86
+ >,
87
+ Pick<ViewProps, 'collapsable'> {
88
+ onNativeAdFailedToLoad?: (e: NativeErrorEvent) => void;
89
+ onNativeAdShowFailed?: (e: NativeErrorEvent) => void;
90
+ /** Android: Fabric에서 View 명령이 무시되는 경우를 피하기 위해 prop으로 전달 */
91
+ loadRequestId?: number;
92
+ showRequestId?: number;
93
+ viewSyncKey?: number;
69
94
  }
70
95
 
71
96
  const VIEW_MANAGER_NAME = 'RNAdWhaleMediationTemplateNativeAdView';
@@ -77,51 +102,63 @@ export const AdWhaleNativeTemplateView = forwardRef<
77
102
  AdWhaleNativeTemplateHandle,
78
103
  AdWhaleNativeTemplateViewProps
79
104
  >((props, ref) => {
80
- const innerRef = useRef<React.ElementRef<typeof NativeComponent>>(null);
105
+ if (Platform.OS === 'ios') {
106
+ // iOS는 커스텀 바인딩만 지원 (템플릿 바인딩 미지원)
107
+ return null;
108
+ }
109
+ const {
110
+ loadSignal,
111
+ showSignal,
112
+ viewSyncKey,
113
+ onNativeAdFailedToLoad,
114
+ onNativeAdShowFailed,
115
+ ...nativeRest
116
+ } = props;
117
+
118
+ const [internalLoadId, setInternalLoadId] = useState(0);
119
+ const [internalShowId, setInternalShowId] = useState(0);
120
+
121
+ const useExternalLoad = loadSignal !== undefined;
122
+ const useExternalShow = showSignal !== undefined;
123
+ const loadRequestIdForNative = useExternalLoad ? loadSignal : internalLoadId;
124
+ const showRequestIdForNative = useExternalShow ? showSignal : internalShowId;
81
125
 
82
126
  useImperativeHandle(ref, () => ({
83
127
  loadAd() {
84
- const handle = findNodeHandle(innerRef.current);
85
- if (!handle) return;
86
- const config = UIManager.getViewManagerConfig(VIEW_MANAGER_NAME) as
87
- | { Commands?: { loadAd?: number; showAd?: number } }
88
- | undefined;
89
- if (!config?.Commands?.loadAd) return;
90
- UIManager.dispatchViewManagerCommand(
91
- handle,
92
- config.Commands.loadAd,
93
- [],
94
- );
128
+ if (useExternalLoad) {
129
+ return;
130
+ }
131
+ setInternalLoadId(v => v + 1);
95
132
  },
96
133
  showAd() {
97
- const handle = findNodeHandle(innerRef.current);
98
- if (!handle) return;
99
- const config = UIManager.getViewManagerConfig(VIEW_MANAGER_NAME) as
100
- | { Commands?: { loadAd?: number; showAd?: number } }
101
- | undefined;
102
- if (!config?.Commands?.showAd) return;
103
- UIManager.dispatchViewManagerCommand(
104
- handle,
105
- config.Commands.showAd,
106
- [],
107
- );
134
+ if (useExternalShow) {
135
+ return;
136
+ }
137
+ setInternalShowId(v => v + 1);
108
138
  },
109
139
  }));
110
140
 
111
- const handleFailedToLoad: NativeProps['onAdFailedToLoad'] = e => {
112
- props.onAdFailedToLoad?.(e.nativeEvent);
141
+ const handleFailedToLoad: NativeProps['onNativeAdFailedToLoad'] = e => {
142
+ onNativeAdFailedToLoad?.(e.nativeEvent);
113
143
  };
114
144
 
115
- const handleShowFailed: NativeProps['onAdShowFailed'] = e => {
116
- props.onAdShowFailed?.(e.nativeEvent);
145
+ const handleShowFailed: NativeProps['onNativeAdShowFailed'] = e => {
146
+ onNativeAdShowFailed?.(e.nativeEvent);
117
147
  };
118
148
 
119
149
  return (
120
150
  <NativeComponent
121
- ref={innerRef}
122
- {...props}
123
- onAdFailedToLoad={props.onAdFailedToLoad ? handleFailedToLoad : undefined}
124
- onAdShowFailed={props.onAdShowFailed ? handleShowFailed : undefined}
151
+ collapsable={false}
152
+ {...nativeRest}
153
+ viewSyncKey={viewSyncKey ?? -1}
154
+ loadRequestId={loadRequestIdForNative}
155
+ showRequestId={showRequestIdForNative}
156
+ onNativeAdFailedToLoad={
157
+ onNativeAdFailedToLoad ? handleFailedToLoad : undefined
158
+ }
159
+ onNativeAdShowFailed={
160
+ onNativeAdShowFailed ? handleShowFailed : undefined
161
+ }
125
162
  />
126
163
  );
127
164
  });
@@ -8,8 +8,14 @@ export interface AdWhaleRewardErrorEvent {
8
8
  }
9
9
 
10
10
  export interface AdWhaleRewardUserRewardedEvent {
11
- type: string;
12
- amount: number;
11
+ /** Flutter payload key */
12
+ RewardType?: string;
13
+ /** Flutter payload key */
14
+ RewardAmount?: number;
15
+ /** 레거시 호환 */
16
+ type?: string;
17
+ /** 레거시 호환 */
18
+ amount?: number;
13
19
  }
14
20
 
15
21
  export interface AdWhaleRewardEvents {
@@ -28,20 +34,34 @@ export interface AdWhaleRewardGcoder {
28
34
  }
29
35
 
30
36
  interface RNAdWhaleMediationRewardAdNativeModule {
31
- setPlacementName(placementName: string): void;
32
- setRegion(region: string): void;
33
- setGcoder(gcoder: { lt: number; lng: number }): void;
34
- loadAd(placementUid: string): void;
35
- showAd(): void;
37
+ setPlacementName?(placementName: string): void;
38
+ setRegion?(region: string): void;
39
+ setGcoder?(gcoder: { lt: number; lng: number }): void;
40
+ loadAd?(placementUid: string): void;
41
+ showAd?(): void;
42
+ loadRewardAd?(args: {
43
+ adId: number;
44
+ placementUid: string;
45
+ placementName?: string;
46
+ region?: string;
47
+ gcoder?: [number, number];
48
+ }): void;
49
+ showAdWithoutView?(adId: number): void;
50
+ destroyAd?(adId: number): void;
36
51
  addListener(eventName: string): void;
37
52
  removeListeners(count: number): void;
38
53
  }
39
54
 
55
+ const LINKING_ERROR =
56
+ 'RNAdWhaleMediationRewardAd native module is not linked on this platform.';
57
+
40
58
  const { RNAdWhaleMediationRewardAd } = NativeModules as {
41
- RNAdWhaleMediationRewardAd: RNAdWhaleMediationRewardAdNativeModule;
59
+ RNAdWhaleMediationRewardAd?: RNAdWhaleMediationRewardAdNativeModule;
42
60
  };
43
61
 
44
- const rewardEmitter = new NativeEventEmitter(RNAdWhaleMediationRewardAd);
62
+ const rewardEmitter = RNAdWhaleMediationRewardAd
63
+ ? new NativeEventEmitter(RNAdWhaleMediationRewardAd)
64
+ : null;
45
65
 
46
66
  export const AdWhaleRewardAd = {
47
67
  loadAd(
@@ -52,34 +72,119 @@ export const AdWhaleRewardAd = {
52
72
  gcoder?: AdWhaleRewardGcoder;
53
73
  },
54
74
  ) {
75
+ if (!RNAdWhaleMediationRewardAd) {
76
+ console.warn(LINKING_ERROR);
77
+ return;
78
+ }
55
79
  // 먼저 옵션들을 설정
56
80
  if (options?.placementName) {
57
- RNAdWhaleMediationRewardAd.setPlacementName(options.placementName);
81
+ RNAdWhaleMediationRewardAd.setPlacementName?.(options.placementName);
58
82
  }
59
83
  if (options?.region) {
60
- RNAdWhaleMediationRewardAd.setRegion(options.region);
84
+ RNAdWhaleMediationRewardAd.setRegion?.(options.region);
61
85
  }
62
86
  if (options?.gcoder) {
63
- RNAdWhaleMediationRewardAd.setGcoder(options.gcoder);
87
+ RNAdWhaleMediationRewardAd.setGcoder?.(options.gcoder);
88
+ }
89
+ if (RNAdWhaleMediationRewardAd.loadAd) {
90
+ RNAdWhaleMediationRewardAd.loadAd(placementUid);
91
+ return;
92
+ }
93
+ if (RNAdWhaleMediationRewardAd.loadRewardAd) {
94
+ RNAdWhaleMediationRewardAd.loadRewardAd({
95
+ adId: 0,
96
+ placementUid,
97
+ placementName: options?.placementName,
98
+ region: options?.region,
99
+ gcoder: options?.gcoder
100
+ ? [options.gcoder.lt, options.gcoder.lng]
101
+ : undefined,
102
+ });
103
+ return;
64
104
  }
65
- // 그 다음 loadAd 호출
66
- RNAdWhaleMediationRewardAd.loadAd(placementUid);
105
+ console.warn(
106
+ '[AdWhaleRewardAd] Native module missing both loadAd and loadRewardAd.',
107
+ );
67
108
  },
68
109
 
69
110
  showAd() {
70
- RNAdWhaleMediationRewardAd.showAd();
111
+ if (!RNAdWhaleMediationRewardAd) {
112
+ console.warn(LINKING_ERROR);
113
+ return;
114
+ }
115
+ if (RNAdWhaleMediationRewardAd.showAd) {
116
+ RNAdWhaleMediationRewardAd.showAd();
117
+ return;
118
+ }
119
+ if (RNAdWhaleMediationRewardAd.showAdWithoutView) {
120
+ RNAdWhaleMediationRewardAd.showAdWithoutView(0);
121
+ return;
122
+ }
123
+ console.warn(
124
+ '[AdWhaleRewardAd] Native module missing both showAd and showAdWithoutView.',
125
+ );
126
+ },
127
+
128
+ /** Flutter와 동일한 신규 API (권장) */
129
+ loadRewardAd(
130
+ adId: number,
131
+ placementUid: string,
132
+ options?: {
133
+ placementName?: string;
134
+ region?: string;
135
+ gcoder?: AdWhaleRewardGcoder;
136
+ },
137
+ ) {
138
+ if (!RNAdWhaleMediationRewardAd) {
139
+ console.warn(LINKING_ERROR);
140
+ return;
141
+ }
142
+ if (!RNAdWhaleMediationRewardAd.loadRewardAd) {
143
+ this.loadAd(placementUid, options);
144
+ return;
145
+ }
146
+ RNAdWhaleMediationRewardAd.loadRewardAd({
147
+ adId,
148
+ placementUid,
149
+ placementName: options?.placementName,
150
+ region: options?.region,
151
+ gcoder: options?.gcoder ? [options.gcoder.lt, options.gcoder.lng] : undefined,
152
+ });
153
+ },
154
+
155
+ showAdWithoutView(adId: number) {
156
+ if (!RNAdWhaleMediationRewardAd) {
157
+ console.warn(LINKING_ERROR);
158
+ return;
159
+ }
160
+ if (!RNAdWhaleMediationRewardAd.showAdWithoutView) {
161
+ this.showAd();
162
+ return;
163
+ }
164
+ RNAdWhaleMediationRewardAd.showAdWithoutView(adId);
165
+ },
166
+
167
+ destroyAd(adId: number) {
168
+ RNAdWhaleMediationRewardAd?.destroyAd?.(adId);
71
169
  },
72
170
 
73
171
  addEventListeners(listeners: AdWhaleRewardEvents): EmitterSubscription[] {
172
+ if (!rewardEmitter) {
173
+ console.warn(LINKING_ERROR);
174
+ return [];
175
+ }
74
176
  const subs: EmitterSubscription[] = [];
177
+ let directShowedReceived = false;
178
+ let directUserRewardedReceived = false;
75
179
 
76
- if (listeners.onLoaded) {
77
- subs.push(
78
- rewardEmitter.addListener('onRewardAdLoaded', () => {
79
- listeners.onLoaded?.();
80
- }),
81
- );
82
- }
180
+ subs.push(
181
+ rewardEmitter.addListener('onRewardAdLoaded', () => {
182
+ directShowedReceived = false;
183
+ directUserRewardedReceived = false;
184
+ console.log('AdWhaleRewardAd.ts Reward onLoaded');
185
+ listeners.onLoaded?.();
186
+ }),
187
+ );
83
188
  if (listeners.onLoadFailed) {
84
189
  subs.push(
85
190
  rewardEmitter.addListener(
@@ -88,13 +193,13 @@ export const AdWhaleRewardAd = {
88
193
  ),
89
194
  );
90
195
  }
91
- if (listeners.onShowed) {
92
- subs.push(
93
- rewardEmitter.addListener('onRewardAdShowed', () => {
94
- listeners.onShowed?.();
95
- }),
96
- );
97
- }
196
+ subs.push(
197
+ rewardEmitter.addListener('onRewardAdShowed', () => {
198
+ directShowedReceived = true;
199
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
200
+ listeners.onShowed?.();
201
+ }),
202
+ );
98
203
  if (listeners.onShowFailed) {
99
204
  subs.push(
100
205
  rewardEmitter.addListener(
@@ -103,13 +208,14 @@ export const AdWhaleRewardAd = {
103
208
  ),
104
209
  );
105
210
  }
106
- if (listeners.onDismissed) {
107
- subs.push(
108
- rewardEmitter.addListener('onRewardAdDismissed', () => {
109
- listeners.onDismissed?.();
110
- }),
111
- );
112
- }
211
+ subs.push(
212
+ rewardEmitter.addListener('onRewardAdDismissed', () => {
213
+ directShowedReceived = false;
214
+ directUserRewardedReceived = false;
215
+ console.log('AdWhaleRewardAd.ts Reward onDismissed');
216
+ listeners.onDismissed?.();
217
+ }),
218
+ );
113
219
  if (listeners.onClicked) {
114
220
  subs.push(
115
221
  rewardEmitter.addListener('onRewardAdClicked', () => {
@@ -117,15 +223,111 @@ export const AdWhaleRewardAd = {
117
223
  }),
118
224
  );
119
225
  }
120
- if (listeners.onUserRewarded) {
226
+ subs.push(
227
+ rewardEmitter.addListener(
228
+ 'onUserRewarded',
229
+ event => {
230
+ directUserRewardedReceived = true;
231
+ const rewardedEvent = event as AdWhaleRewardUserRewardedEvent;
232
+ const rewardAmount = rewardedEvent.amount ?? rewardedEvent.RewardAmount;
233
+ const rewardType = rewardedEvent.type ?? rewardedEvent.RewardType;
234
+ console.log(
235
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
236
+ );
237
+ listeners.onUserRewarded?.(rewardedEvent);
238
+ },
239
+ ),
240
+ );
241
+ subs.push(
242
+ rewardEmitter.addListener('onAdEvent', (payload: any) => {
243
+ if (payload?.eventName === 'onRewardAdShowed' && !directShowedReceived) {
244
+ directShowedReceived = true;
245
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
246
+ listeners.onShowed?.();
247
+ return;
248
+ }
249
+
250
+ if (payload?.eventName === 'onUserRewarded' && !directUserRewardedReceived) {
251
+ directUserRewardedReceived = true;
252
+ const rewardAmount = payload?.amount ?? payload?.RewardAmount;
253
+ const rewardType = payload?.type ?? payload?.RewardType;
254
+ console.log(
255
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
256
+ );
257
+ listeners.onUserRewarded?.({
258
+ RewardType: payload?.RewardType,
259
+ RewardAmount: payload?.RewardAmount,
260
+ type: payload?.type,
261
+ amount: payload?.amount,
262
+ });
263
+ }
264
+ }),
265
+ );
266
+
267
+ return subs;
268
+ },
269
+
270
+ /** Flutter `onAdEvent` 스타일 (adId 필터링) */
271
+ addAdEventListenersById(
272
+ adId: number,
273
+ listeners: AdWhaleRewardEvents,
274
+ ): EmitterSubscription[] {
275
+ if (!rewardEmitter) {
276
+ console.warn(LINKING_ERROR);
277
+ return [];
278
+ }
279
+ const subs: EmitterSubscription[] = [];
280
+
281
+ const handle = (eventName: string, cb?: (p?: any) => void) => {
282
+ if (!cb) return;
121
283
  subs.push(
122
- rewardEmitter.addListener(
123
- 'onUserRewarded',
124
- event =>
125
- listeners.onUserRewarded?.(event as AdWhaleRewardUserRewardedEvent),
126
- ),
284
+ rewardEmitter.addListener('onAdEvent', (payload: any) => {
285
+ if (!payload || payload.adId !== adId || payload.eventName !== eventName) {
286
+ return;
287
+ }
288
+ cb(payload);
289
+ }),
127
290
  );
128
- }
291
+ };
292
+
293
+ handle('onRewardAdLoaded', () => {
294
+ console.log('AdWhaleRewardAd.ts Reward onLoaded');
295
+ listeners.onLoaded?.();
296
+ });
297
+ handle('onRewardAdFailedToLoad', p =>
298
+ listeners.onLoadFailed?.({
299
+ statusCode: Number(p?.statusCode ?? -1),
300
+ message: String(p?.message ?? ''),
301
+ }),
302
+ );
303
+ handle('onRewardAdShowed', () => {
304
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
305
+ listeners.onShowed?.();
306
+ });
307
+ handle('onRewardAdFailedToShow', p =>
308
+ listeners.onShowFailed?.({
309
+ statusCode: Number(p?.statusCode ?? -1),
310
+ message: String(p?.message ?? ''),
311
+ }),
312
+ );
313
+ handle('onRewardAdDismissed', () => {
314
+ console.log('AdWhaleRewardAd.ts Reward onDismissed');
315
+ listeners.onDismissed?.();
316
+ });
317
+ handle('onRewardAdClicked', () => listeners.onClicked?.());
318
+ handle('onUserRewarded', p => {
319
+ const rewardAmount = p?.amount ?? p?.RewardAmount;
320
+ const rewardType = p?.type ?? p?.RewardType;
321
+ console.log(
322
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
323
+ );
324
+ listeners.onUserRewarded?.({
325
+ RewardType: p?.RewardType,
326
+ RewardAmount: p?.RewardAmount,
327
+ type: p?.type,
328
+ amount: p?.amount,
329
+ });
330
+ });
129
331
 
130
332
  return subs;
131
333
  },
@@ -0,0 +1,171 @@
1
+ // src/AdWhaleTransitionPopupAd.ts
2
+ import {
3
+ NativeEventEmitter,
4
+ NativeModules,
5
+ Platform,
6
+ } from 'react-native';
7
+ import type { EmitterSubscription } from 'react-native';
8
+
9
+ export interface AdWhaleTransitionPopupAdErrorEvent {
10
+ statusCode: number;
11
+ message: string;
12
+ }
13
+
14
+ export interface AdWhaleTransitionPopupAdClosedEvent {
15
+ reasonId: number;
16
+ reasonMessage: string;
17
+ }
18
+
19
+ export interface AdWhaleTransitionPopupAdEvents {
20
+ onLoaded?: () => void;
21
+ onLoadFailed?: (event: AdWhaleTransitionPopupAdErrorEvent) => void;
22
+ onShowed?: () => void;
23
+ onShowFailed?: (event: AdWhaleTransitionPopupAdErrorEvent) => void;
24
+ onClicked?: () => void;
25
+ onClosed?: (event: AdWhaleTransitionPopupAdClosedEvent) => void;
26
+ }
27
+
28
+ export interface AdWhaleTransitionPopupGcoder {
29
+ lt: number;
30
+ lng: number;
31
+ }
32
+
33
+ interface RNAdWhaleMediationTransitionPopupAdNativeModule {
34
+ setPlacementName(placementName: string): void;
35
+ setRegion(region: string): void;
36
+ setGcoder(gcoder: { lt: number; lng: number }): void;
37
+ loadAd(placementUid: string): void;
38
+ showAd(): void;
39
+ destroy(): void;
40
+ resume(): void;
41
+ addListener(eventName: string): void;
42
+ removeListeners(count: number): void;
43
+ }
44
+
45
+ const LINKING_ERROR =
46
+ Platform.OS === 'android'
47
+ ? 'RNAdWhaleMediationTransitionPopupAd native module is not linked.'
48
+ : 'Transition Popup Ad is only supported on Android.';
49
+
50
+ const { RNAdWhaleMediationTransitionPopupAd } = NativeModules as {
51
+ RNAdWhaleMediationTransitionPopupAd?: RNAdWhaleMediationTransitionPopupAdNativeModule;
52
+ };
53
+
54
+ const transitionPopupEmitter = RNAdWhaleMediationTransitionPopupAd
55
+ ? new NativeEventEmitter(RNAdWhaleMediationTransitionPopupAd)
56
+ : null;
57
+
58
+ export const AdWhaleTransitionPopupAd = {
59
+ loadAd(
60
+ placementUid: string,
61
+ options?: {
62
+ placementName?: string;
63
+ region?: string;
64
+ gcoder?: AdWhaleTransitionPopupGcoder;
65
+ }
66
+ ) {
67
+ if (!RNAdWhaleMediationTransitionPopupAd) {
68
+ console.warn(LINKING_ERROR);
69
+ return;
70
+ }
71
+ if (options?.placementName != null) {
72
+ RNAdWhaleMediationTransitionPopupAd.setPlacementName(options.placementName);
73
+ }
74
+ if (options?.region != null) {
75
+ RNAdWhaleMediationTransitionPopupAd.setRegion(options.region);
76
+ }
77
+ if (options?.gcoder) {
78
+ RNAdWhaleMediationTransitionPopupAd.setGcoder(options.gcoder);
79
+ }
80
+ RNAdWhaleMediationTransitionPopupAd.loadAd(placementUid);
81
+ },
82
+
83
+ showAd() {
84
+ if (!RNAdWhaleMediationTransitionPopupAd) {
85
+ console.warn(LINKING_ERROR);
86
+ return;
87
+ }
88
+ RNAdWhaleMediationTransitionPopupAd.showAd();
89
+ },
90
+
91
+ destroy() {
92
+ if (!RNAdWhaleMediationTransitionPopupAd) {
93
+ console.warn(LINKING_ERROR);
94
+ return;
95
+ }
96
+ RNAdWhaleMediationTransitionPopupAd.destroy();
97
+ },
98
+
99
+ resume() {
100
+ if (!RNAdWhaleMediationTransitionPopupAd) {
101
+ console.warn(LINKING_ERROR);
102
+ return;
103
+ }
104
+ RNAdWhaleMediationTransitionPopupAd.resume();
105
+ },
106
+
107
+ addEventListeners(
108
+ listeners: AdWhaleTransitionPopupAdEvents
109
+ ): EmitterSubscription[] {
110
+ if (!transitionPopupEmitter) {
111
+ console.warn(LINKING_ERROR);
112
+ return [];
113
+ }
114
+ const subs: EmitterSubscription[] = [];
115
+
116
+ if (listeners.onLoaded) {
117
+ subs.push(
118
+ transitionPopupEmitter.addListener('onTransitionPopupAdLoaded', () => {
119
+ listeners.onLoaded?.();
120
+ })
121
+ );
122
+ }
123
+ if (listeners.onLoadFailed) {
124
+ subs.push(
125
+ transitionPopupEmitter.addListener(
126
+ 'onTransitionPopupAdLoadFailed',
127
+ event => {
128
+ listeners.onLoadFailed?.(
129
+ event as AdWhaleTransitionPopupAdErrorEvent
130
+ );
131
+ }
132
+ )
133
+ );
134
+ }
135
+ if (listeners.onShowed) {
136
+ subs.push(
137
+ transitionPopupEmitter.addListener('onTransitionPopupAdShowed', () => {
138
+ listeners.onShowed?.();
139
+ })
140
+ );
141
+ }
142
+ if (listeners.onShowFailed) {
143
+ subs.push(
144
+ transitionPopupEmitter.addListener(
145
+ 'onTransitionPopupAdShowFailed',
146
+ event => {
147
+ listeners.onShowFailed?.(
148
+ event as AdWhaleTransitionPopupAdErrorEvent
149
+ );
150
+ }
151
+ )
152
+ );
153
+ }
154
+ if (listeners.onClicked) {
155
+ subs.push(
156
+ transitionPopupEmitter.addListener('onTransitionPopupAdClicked', () => {
157
+ listeners.onClicked?.();
158
+ })
159
+ );
160
+ }
161
+ if (listeners.onClosed) {
162
+ subs.push(
163
+ transitionPopupEmitter.addListener('onTransitionPopupAdClosed', event => {
164
+ listeners.onClosed?.(event as AdWhaleTransitionPopupAdClosedEvent);
165
+ })
166
+ );
167
+ }
168
+
169
+ return subs;
170
+ },
171
+ };