adwhale-sdk-react-native 2.7.204 → 2.7.400

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 (109) hide show
  1. package/AdwhaleSdkReactNative.podspec +22 -0
  2. package/README.md +95 -91
  3. package/android/build.gradle +35 -1
  4. package/android/proguard-rules.pro +58 -0
  5. package/android/src/main/java/com/adwhalesdkreactnative/AdWhaleCustomNativeBinderFactory.java +18 -0
  6. package/android/src/main/java/com/adwhalesdkreactnative/AdwhaleSdkReactNativePackage.java +51 -6
  7. package/android/src/main/java/com/adwhalesdkreactnative/BinderFactory.java +9 -0
  8. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdSettingModule.java +148 -49
  9. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdView.java +46 -27
  10. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAppOpenAd.java +148 -0
  11. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationCustomNativeAdView.java +37 -46
  12. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationExitPopupAd.java +240 -0
  13. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationInterstitialAd.java +205 -0
  14. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationLoggerModule.java +2 -1
  15. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationNativeAdViewListenerBridge.java +83 -0
  16. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationRewardAd.java +341 -6
  17. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTemplateNativeAdView.java +191 -70
  18. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTransitionPopupAd.java +218 -0
  19. package/android/src/main/java/com/adwhalesdkreactnative/RNWrapperView.java +137 -0
  20. package/android/src/main/java/com/adwhalesdkreactnative/SimpleBinderFactory.java +1 -1
  21. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodArgConst.java +112 -0
  22. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodCallConst.java +113 -0
  23. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodResultConst.java +28 -0
  24. package/build/generated/ios/ReactAppDependencyProvider.podspec +34 -0
  25. package/ios/AdWhaleNativeCustomViewFactoryRegistry.swift +56 -0
  26. package/ios/AdwhaleSdkReactNative.mm +96 -5
  27. package/ios/AdwhaleSdkReactNativeModule.swift +148 -0
  28. package/ios/RNAdWhaleMediationAdViewManager.m +38 -0
  29. package/ios/RNAdWhaleMediationAdViewManager.swift +14 -0
  30. package/ios/RNAdWhaleMediationAppOpenAd.m +18 -0
  31. package/ios/RNAdWhaleMediationAppOpenAd.swift +175 -0
  32. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.m +22 -0
  33. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.swift +239 -0
  34. package/ios/RNAdWhaleMediationInterstitialAd.m +18 -0
  35. package/ios/RNAdWhaleMediationInterstitialAd.swift +161 -0
  36. package/ios/RNAdWhaleMediationRewardAd.m +22 -0
  37. package/ios/RNAdWhaleMediationRewardAd.swift +206 -0
  38. package/ios/WhaleMediationBannerContainer.swift +182 -0
  39. package/lib/module/AdWhaleAdView.js +58 -3
  40. package/lib/module/AdWhaleAdView.js.map +1 -1
  41. package/lib/module/AdWhaleAppOpenAd.js +223 -25
  42. package/lib/module/AdWhaleAppOpenAd.js.map +1 -1
  43. package/lib/module/AdWhaleExitPopupAd.js +93 -0
  44. package/lib/module/AdWhaleExitPopupAd.js.map +1 -0
  45. package/lib/module/AdWhaleInterstitialAd.js +146 -22
  46. package/lib/module/AdWhaleInterstitialAd.js.map +1 -1
  47. package/lib/module/AdWhaleMediationAds.js +75 -4
  48. package/lib/module/AdWhaleMediationAds.js.map +1 -1
  49. package/lib/module/AdWhaleNativeCustomView.js +59 -8
  50. package/lib/module/AdWhaleNativeCustomView.js.map +1 -1
  51. package/lib/module/AdWhaleNativeTemplateView.js +37 -19
  52. package/lib/module/AdWhaleNativeTemplateView.js.map +1 -1
  53. package/lib/module/AdWhaleRewardAd.js +221 -24
  54. package/lib/module/AdWhaleRewardAd.js.map +1 -1
  55. package/lib/module/AdWhaleTransitionPopupAd.js +87 -0
  56. package/lib/module/AdWhaleTransitionPopupAd.js.map +1 -0
  57. package/lib/module/NativeAdwhaleSdkReactNative.js +16 -1
  58. package/lib/module/NativeAdwhaleSdkReactNative.js.map +1 -1
  59. package/lib/module/constants/AdWhaleMethodArgConst.js +53 -0
  60. package/lib/module/constants/AdWhaleMethodArgConst.js.map +1 -0
  61. package/lib/module/constants/AdWhaleMethodCallConst.js +56 -0
  62. package/lib/module/constants/AdWhaleMethodCallConst.js.map +1 -0
  63. package/lib/module/constants/AdWhaleMethodResultConst.js +16 -0
  64. package/lib/module/constants/AdWhaleMethodResultConst.js.map +1 -0
  65. package/lib/module/index.js +10 -1
  66. package/lib/module/index.js.map +1 -1
  67. package/lib/typescript/src/AdWhaleAdView.d.ts +20 -2
  68. package/lib/typescript/src/AdWhaleAdView.d.ts.map +1 -1
  69. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts +10 -0
  70. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts.map +1 -1
  71. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts +36 -0
  72. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts.map +1 -0
  73. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts +16 -0
  74. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts.map +1 -1
  75. package/lib/typescript/src/AdWhaleMediationAds.d.ts +13 -5
  76. package/lib/typescript/src/AdWhaleMediationAds.d.ts.map +1 -1
  77. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts +7 -4
  78. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts.map +1 -1
  79. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts +20 -7
  80. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts.map +1 -1
  81. package/lib/typescript/src/AdWhaleRewardAd.d.ts +63 -6
  82. package/lib/typescript/src/AdWhaleRewardAd.d.ts.map +1 -1
  83. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts +33 -0
  84. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts.map +1 -0
  85. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts +28 -6
  86. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts.map +1 -1
  87. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts +51 -0
  88. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts.map +1 -0
  89. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts +53 -0
  90. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts.map +1 -0
  91. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts +14 -0
  92. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts.map +1 -0
  93. package/lib/typescript/src/index.d.ts +10 -2
  94. package/lib/typescript/src/index.d.ts.map +1 -1
  95. package/package.json +1 -1
  96. package/src/AdWhaleAdView.tsx +92 -4
  97. package/src/AdWhaleAppOpenAd.ts +293 -38
  98. package/src/AdWhaleExitPopupAd.ts +183 -0
  99. package/src/AdWhaleInterstitialAd.ts +206 -36
  100. package/src/AdWhaleMediationAds.ts +108 -4
  101. package/src/AdWhaleNativeCustomView.tsx +85 -23
  102. package/src/AdWhaleNativeTemplateView.tsx +79 -42
  103. package/src/AdWhaleRewardAd.ts +317 -51
  104. package/src/AdWhaleTransitionPopupAd.ts +171 -0
  105. package/src/NativeAdwhaleSdkReactNative.ts +30 -6
  106. package/src/constants/AdWhaleMethodArgConst.ts +50 -0
  107. package/src/constants/AdWhaleMethodCallConst.ts +60 -0
  108. package/src/constants/AdWhaleMethodResultConst.ts +13 -0
  109. package/src/index.ts +35 -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 {
@@ -27,59 +33,222 @@ export interface AdWhaleRewardGcoder {
27
33
  lng: number;
28
34
  }
29
35
 
36
+ /**
37
+ * 보상형 광고 로드 시 함께 지정할 수 있는 옵션.
38
+ * placementName / region / gcoder 는 기존 옵션이며,
39
+ * userId / customData 는 보상형 SSV(서버 사이드 검증) 전용 옵션입니다.
40
+ */
41
+ export interface AdWhaleRewardLoadOptions {
42
+ placementName?: string;
43
+ region?: string;
44
+ gcoder?: AdWhaleRewardGcoder;
45
+ /**
46
+ * 보상형 SSV(서버 사이드 검증)용 앱 사용자 id.
47
+ * AdMob / AdManager SSV postback 의 user_id 로 전달됩니다.
48
+ * (그 외 네트워크에서는 무시됩니다.)
49
+ */
50
+ userId?: string;
51
+ /**
52
+ * 보상형 SSV(서버 사이드 검증)용 커스텀 데이터.
53
+ * AdMob / AdManager SSV postback 의 custom_data 로 전달되며,
54
+ * 네이티브에서 JSON 문자열로 직렬화되어 실립니다.
55
+ * (그 외 네트워크에서는 무시됩니다.)
56
+ */
57
+ customData?: { [key: string]: string };
58
+ }
59
+
30
60
  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;
61
+ setPlacementName?(placementName: string): void;
62
+ setRegion?(region: string): void;
63
+ setGcoder?(gcoder: { lt: number; lng: number }): void;
64
+ setUserId?(userId: string): void;
65
+ setCustomData?(customData: { [key: string]: string }): void;
66
+ loadAd?(placementUid: string): void;
67
+ showAd?(): void;
68
+ loadRewardAd?(args: {
69
+ adId: number;
70
+ placementUid: string;
71
+ placementName?: string;
72
+ region?: string;
73
+ gcoder?: [number, number];
74
+ userId?: string;
75
+ customData?: { [key: string]: string };
76
+ }): void;
77
+ showAdWithoutView?(adId: number): void;
78
+ destroyAd?(adId: number): void;
36
79
  addListener(eventName: string): void;
37
80
  removeListeners(count: number): void;
38
81
  }
39
82
 
83
+ const LINKING_ERROR =
84
+ 'RNAdWhaleMediationRewardAd native module is not linked on this platform.';
85
+
40
86
  const { RNAdWhaleMediationRewardAd } = NativeModules as {
41
- RNAdWhaleMediationRewardAd: RNAdWhaleMediationRewardAdNativeModule;
87
+ RNAdWhaleMediationRewardAd?: RNAdWhaleMediationRewardAdNativeModule;
42
88
  };
43
89
 
44
- const rewardEmitter = new NativeEventEmitter(RNAdWhaleMediationRewardAd);
90
+ const rewardEmitter = RNAdWhaleMediationRewardAd
91
+ ? new NativeEventEmitter(RNAdWhaleMediationRewardAd)
92
+ : null;
45
93
 
46
94
  export const AdWhaleRewardAd = {
47
- loadAd(
48
- placementUid: string,
49
- options?: {
50
- placementName?: string;
51
- region?: string;
52
- gcoder?: AdWhaleRewardGcoder;
53
- },
54
- ) {
95
+ /**
96
+ * 보상형 SSV(서버 사이드 검증)용 앱 사용자 id 설정.
97
+ *
98
+ * - AdMob / AdManager 의 SSV postback `user_id` 로 전달됩니다. 그 외 네트워크에서는 무시(no-op)됩니다.
99
+ * - `loadAd()` 호출 전에 설정하는 것을 권장하며, `showAd()` 이전까지 호출할 수 있습니다.
100
+ * - 여러 번 호출하면 마지막 값이 적용됩니다.
101
+ *
102
+ * 참고: `loadAd(placementUid, { userId })` 옵션으로도 동일하게 지정할 수 있습니다.
103
+ */
104
+ setUserId(userId: string) {
105
+ if (!RNAdWhaleMediationRewardAd) {
106
+ console.warn(LINKING_ERROR);
107
+ return;
108
+ }
109
+ RNAdWhaleMediationRewardAd.setUserId?.(userId);
110
+ },
111
+
112
+ /**
113
+ * 보상형 SSV(서버 사이드 검증)용 커스텀 데이터 설정.
114
+ *
115
+ * - AdMob / AdManager 의 SSV postback `custom_data` 로 전달됩니다. 그 외 네트워크에서는 무시(no-op)됩니다.
116
+ * - 전달한 객체는 네이티브에서 JSON 문자열로 직렬화되어 단일 문자열 필드에 실립니다.
117
+ * 퍼블리셔 서버는 postback 의 `custom_data` 값을 JSON 으로 파싱해야 합니다.
118
+ * - AdMob 의 `custom_data` 는 실무상 200자 정도를 넘기면 잘릴 수 있으니 길이에 유의하세요.
119
+ * - `loadAd()` 호출 전에 설정하는 것을 권장하며, `showAd()` 이전까지 호출할 수 있습니다.
120
+ *
121
+ * 참고: `loadAd(placementUid, { customData })` 옵션으로도 동일하게 지정할 수 있습니다.
122
+ */
123
+ setCustomData(customData: { [key: string]: string }) {
124
+ if (!RNAdWhaleMediationRewardAd) {
125
+ console.warn(LINKING_ERROR);
126
+ return;
127
+ }
128
+ RNAdWhaleMediationRewardAd.setCustomData?.(customData);
129
+ },
130
+
131
+ loadAd(placementUid: string, options?: AdWhaleRewardLoadOptions) {
132
+ if (!RNAdWhaleMediationRewardAd) {
133
+ console.warn(LINKING_ERROR);
134
+ return;
135
+ }
55
136
  // 먼저 옵션들을 설정
56
137
  if (options?.placementName) {
57
- RNAdWhaleMediationRewardAd.setPlacementName(options.placementName);
138
+ RNAdWhaleMediationRewardAd.setPlacementName?.(options.placementName);
58
139
  }
59
140
  if (options?.region) {
60
- RNAdWhaleMediationRewardAd.setRegion(options.region);
141
+ RNAdWhaleMediationRewardAd.setRegion?.(options.region);
61
142
  }
62
143
  if (options?.gcoder) {
63
- RNAdWhaleMediationRewardAd.setGcoder(options.gcoder);
144
+ RNAdWhaleMediationRewardAd.setGcoder?.(options.gcoder);
145
+ }
146
+ // 보상형 SSV(서버 사이드 검증) 옵션
147
+ if (options?.userId) {
148
+ RNAdWhaleMediationRewardAd.setUserId?.(options.userId);
149
+ }
150
+ if (options?.customData) {
151
+ RNAdWhaleMediationRewardAd.setCustomData?.(options.customData);
152
+ }
153
+ if (RNAdWhaleMediationRewardAd.loadAd) {
154
+ RNAdWhaleMediationRewardAd.loadAd(placementUid);
155
+ return;
64
156
  }
65
- // 다음 loadAd 호출
66
- RNAdWhaleMediationRewardAd.loadAd(placementUid);
157
+ if (RNAdWhaleMediationRewardAd.loadRewardAd) {
158
+ RNAdWhaleMediationRewardAd.loadRewardAd({
159
+ adId: 0,
160
+ placementUid,
161
+ placementName: options?.placementName,
162
+ region: options?.region,
163
+ gcoder: options?.gcoder
164
+ ? [options.gcoder.lt, options.gcoder.lng]
165
+ : undefined,
166
+ userId: options?.userId,
167
+ customData: options?.customData,
168
+ });
169
+ return;
170
+ }
171
+ console.warn(
172
+ '[AdWhaleRewardAd] Native module missing both loadAd and loadRewardAd.',
173
+ );
67
174
  },
68
175
 
69
176
  showAd() {
70
- RNAdWhaleMediationRewardAd.showAd();
177
+ if (!RNAdWhaleMediationRewardAd) {
178
+ console.warn(LINKING_ERROR);
179
+ return;
180
+ }
181
+ if (RNAdWhaleMediationRewardAd.showAd) {
182
+ RNAdWhaleMediationRewardAd.showAd();
183
+ return;
184
+ }
185
+ if (RNAdWhaleMediationRewardAd.showAdWithoutView) {
186
+ RNAdWhaleMediationRewardAd.showAdWithoutView(0);
187
+ return;
188
+ }
189
+ console.warn(
190
+ '[AdWhaleRewardAd] Native module missing both showAd and showAdWithoutView.',
191
+ );
192
+ },
193
+
194
+ /** Flutter와 동일한 신규 API (권장) */
195
+ loadRewardAd(
196
+ adId: number,
197
+ placementUid: string,
198
+ options?: AdWhaleRewardLoadOptions,
199
+ ) {
200
+ if (!RNAdWhaleMediationRewardAd) {
201
+ console.warn(LINKING_ERROR);
202
+ return;
203
+ }
204
+ if (!RNAdWhaleMediationRewardAd.loadRewardAd) {
205
+ this.loadAd(placementUid, options);
206
+ return;
207
+ }
208
+ RNAdWhaleMediationRewardAd.loadRewardAd({
209
+ adId,
210
+ placementUid,
211
+ placementName: options?.placementName,
212
+ region: options?.region,
213
+ gcoder: options?.gcoder ? [options.gcoder.lt, options.gcoder.lng] : undefined,
214
+ userId: options?.userId,
215
+ customData: options?.customData,
216
+ });
217
+ },
218
+
219
+ showAdWithoutView(adId: number) {
220
+ if (!RNAdWhaleMediationRewardAd) {
221
+ console.warn(LINKING_ERROR);
222
+ return;
223
+ }
224
+ if (!RNAdWhaleMediationRewardAd.showAdWithoutView) {
225
+ this.showAd();
226
+ return;
227
+ }
228
+ RNAdWhaleMediationRewardAd.showAdWithoutView(adId);
229
+ },
230
+
231
+ destroyAd(adId: number) {
232
+ RNAdWhaleMediationRewardAd?.destroyAd?.(adId);
71
233
  },
72
234
 
73
235
  addEventListeners(listeners: AdWhaleRewardEvents): EmitterSubscription[] {
236
+ if (!rewardEmitter) {
237
+ console.warn(LINKING_ERROR);
238
+ return [];
239
+ }
74
240
  const subs: EmitterSubscription[] = [];
241
+ let directShowedReceived = false;
242
+ let directUserRewardedReceived = false;
75
243
 
76
- if (listeners.onLoaded) {
77
- subs.push(
78
- rewardEmitter.addListener('onRewardAdLoaded', () => {
79
- listeners.onLoaded?.();
80
- }),
81
- );
82
- }
244
+ subs.push(
245
+ rewardEmitter.addListener('onRewardAdLoaded', () => {
246
+ directShowedReceived = false;
247
+ directUserRewardedReceived = false;
248
+ console.log('AdWhaleRewardAd.ts Reward onLoaded');
249
+ listeners.onLoaded?.();
250
+ }),
251
+ );
83
252
  if (listeners.onLoadFailed) {
84
253
  subs.push(
85
254
  rewardEmitter.addListener(
@@ -88,13 +257,13 @@ export const AdWhaleRewardAd = {
88
257
  ),
89
258
  );
90
259
  }
91
- if (listeners.onShowed) {
92
- subs.push(
93
- rewardEmitter.addListener('onRewardAdShowed', () => {
94
- listeners.onShowed?.();
95
- }),
96
- );
97
- }
260
+ subs.push(
261
+ rewardEmitter.addListener('onRewardAdShowed', () => {
262
+ directShowedReceived = true;
263
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
264
+ listeners.onShowed?.();
265
+ }),
266
+ );
98
267
  if (listeners.onShowFailed) {
99
268
  subs.push(
100
269
  rewardEmitter.addListener(
@@ -103,13 +272,14 @@ export const AdWhaleRewardAd = {
103
272
  ),
104
273
  );
105
274
  }
106
- if (listeners.onDismissed) {
107
- subs.push(
108
- rewardEmitter.addListener('onRewardAdDismissed', () => {
109
- listeners.onDismissed?.();
110
- }),
111
- );
112
- }
275
+ subs.push(
276
+ rewardEmitter.addListener('onRewardAdDismissed', () => {
277
+ directShowedReceived = false;
278
+ directUserRewardedReceived = false;
279
+ console.log('AdWhaleRewardAd.ts Reward onDismissed');
280
+ listeners.onDismissed?.();
281
+ }),
282
+ );
113
283
  if (listeners.onClicked) {
114
284
  subs.push(
115
285
  rewardEmitter.addListener('onRewardAdClicked', () => {
@@ -117,15 +287,111 @@ export const AdWhaleRewardAd = {
117
287
  }),
118
288
  );
119
289
  }
120
- if (listeners.onUserRewarded) {
290
+ subs.push(
291
+ rewardEmitter.addListener(
292
+ 'onUserRewarded',
293
+ event => {
294
+ directUserRewardedReceived = true;
295
+ const rewardedEvent = event as AdWhaleRewardUserRewardedEvent;
296
+ const rewardAmount = rewardedEvent.amount ?? rewardedEvent.RewardAmount;
297
+ const rewardType = rewardedEvent.type ?? rewardedEvent.RewardType;
298
+ console.log(
299
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
300
+ );
301
+ listeners.onUserRewarded?.(rewardedEvent);
302
+ },
303
+ ),
304
+ );
305
+ subs.push(
306
+ rewardEmitter.addListener('onAdEvent', (payload: any) => {
307
+ if (payload?.eventName === 'onRewardAdShowed' && !directShowedReceived) {
308
+ directShowedReceived = true;
309
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
310
+ listeners.onShowed?.();
311
+ return;
312
+ }
313
+
314
+ if (payload?.eventName === 'onUserRewarded' && !directUserRewardedReceived) {
315
+ directUserRewardedReceived = true;
316
+ const rewardAmount = payload?.amount ?? payload?.RewardAmount;
317
+ const rewardType = payload?.type ?? payload?.RewardType;
318
+ console.log(
319
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
320
+ );
321
+ listeners.onUserRewarded?.({
322
+ RewardType: payload?.RewardType,
323
+ RewardAmount: payload?.RewardAmount,
324
+ type: payload?.type,
325
+ amount: payload?.amount,
326
+ });
327
+ }
328
+ }),
329
+ );
330
+
331
+ return subs;
332
+ },
333
+
334
+ /** Flutter `onAdEvent` 스타일 (adId 필터링) */
335
+ addAdEventListenersById(
336
+ adId: number,
337
+ listeners: AdWhaleRewardEvents,
338
+ ): EmitterSubscription[] {
339
+ if (!rewardEmitter) {
340
+ console.warn(LINKING_ERROR);
341
+ return [];
342
+ }
343
+ const subs: EmitterSubscription[] = [];
344
+
345
+ const handle = (eventName: string, cb?: (p?: any) => void) => {
346
+ if (!cb) return;
121
347
  subs.push(
122
- rewardEmitter.addListener(
123
- 'onUserRewarded',
124
- event =>
125
- listeners.onUserRewarded?.(event as AdWhaleRewardUserRewardedEvent),
126
- ),
348
+ rewardEmitter.addListener('onAdEvent', (payload: any) => {
349
+ if (!payload || payload.adId !== adId || payload.eventName !== eventName) {
350
+ return;
351
+ }
352
+ cb(payload);
353
+ }),
127
354
  );
128
- }
355
+ };
356
+
357
+ handle('onRewardAdLoaded', () => {
358
+ console.log('AdWhaleRewardAd.ts Reward onLoaded');
359
+ listeners.onLoaded?.();
360
+ });
361
+ handle('onRewardAdFailedToLoad', p =>
362
+ listeners.onLoadFailed?.({
363
+ statusCode: Number(p?.statusCode ?? -1),
364
+ message: String(p?.message ?? ''),
365
+ }),
366
+ );
367
+ handle('onRewardAdShowed', () => {
368
+ console.log('AdWhaleRewardAd.ts Reward onAdShowed');
369
+ listeners.onShowed?.();
370
+ });
371
+ handle('onRewardAdFailedToShow', p =>
372
+ listeners.onShowFailed?.({
373
+ statusCode: Number(p?.statusCode ?? -1),
374
+ message: String(p?.message ?? ''),
375
+ }),
376
+ );
377
+ handle('onRewardAdDismissed', () => {
378
+ console.log('AdWhaleRewardAd.ts Reward onDismissed');
379
+ listeners.onDismissed?.();
380
+ });
381
+ handle('onRewardAdClicked', () => listeners.onClicked?.());
382
+ handle('onUserRewarded', p => {
383
+ const rewardAmount = p?.amount ?? p?.RewardAmount;
384
+ const rewardType = p?.type ?? p?.RewardType;
385
+ console.log(
386
+ `AdWhaleRewardAd.ts Reward onUserRewarded: ${String(rewardAmount)}, ${String(rewardType)}`,
387
+ );
388
+ listeners.onUserRewarded?.({
389
+ RewardType: p?.RewardType,
390
+ RewardAmount: p?.RewardAmount,
391
+ type: p?.type,
392
+ amount: p?.amount,
393
+ });
394
+ });
129
395
 
130
396
  return subs;
131
397
  },