@vynix-org/react-native-ads-sdk 0.1.1
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.
- package/AdsSdk.podspec +52 -0
- package/LICENSE +20 -0
- package/README.md +95 -0
- package/android/build.gradle +108 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/adssdk/AdsSdkModule.kt +23 -0
- package/android/src/main/java/com/adssdk/AdsSdkPackage.kt +33 -0
- package/ios/AdsSdk.h +5 -0
- package/ios/AdsSdk.mm +18 -0
- package/lib/module/ads-manager/AppOpenAdsManager.js +131 -0
- package/lib/module/ads-manager/AppOpenAdsManager.js.map +1 -0
- package/lib/module/ads-manager/GoogleMobileAdsManager.js +62 -0
- package/lib/module/ads-manager/GoogleMobileAdsManager.js.map +1 -0
- package/lib/module/ads-manager/InterstitialAdsManager.js +291 -0
- package/lib/module/ads-manager/InterstitialAdsManager.js.map +1 -0
- package/lib/module/ads-manager/NativeAdsManager.js +405 -0
- package/lib/module/ads-manager/NativeAdsManager.js.map +1 -0
- package/lib/module/ads-manager/RewardedAdsManager.js +328 -0
- package/lib/module/ads-manager/RewardedAdsManager.js.map +1 -0
- package/lib/module/ads-manager/RewardedInterstitialAdsManager.js +328 -0
- package/lib/module/ads-manager/RewardedInterstitialAdsManager.js.map +1 -0
- package/lib/module/components/BannerAdComponent.js +159 -0
- package/lib/module/components/BannerAdComponent.js.map +1 -0
- package/lib/module/components/FullscreenStyles.js +162 -0
- package/lib/module/components/FullscreenStyles.js.map +1 -0
- package/lib/module/components/NativeAdComponent.js +172 -0
- package/lib/module/components/NativeAdComponent.js.map +1 -0
- package/lib/module/components/NativeAdFullscreenComponent.js +205 -0
- package/lib/module/components/NativeAdFullscreenComponent.js.map +1 -0
- package/lib/module/components/NativeStyles.js +143 -0
- package/lib/module/components/NativeStyles.js.map +1 -0
- package/lib/module/event/custom-event.js +29 -0
- package/lib/module/event/custom-event.js.map +1 -0
- package/lib/module/hook/useAppOpenAd.js +138 -0
- package/lib/module/hook/useAppOpenAd.js.map +1 -0
- package/lib/module/hook/useInterstitialAd.js +157 -0
- package/lib/module/hook/useInterstitialAd.js.map +1 -0
- package/lib/module/hook/useNativeAd.js +147 -0
- package/lib/module/hook/useNativeAd.js.map +1 -0
- package/lib/module/hook/useRewardedAd.js +166 -0
- package/lib/module/hook/useRewardedAd.js.map +1 -0
- package/lib/module/hook/useRewardedInterstitialAd.js +166 -0
- package/lib/module/hook/useRewardedInterstitialAd.js.map +1 -0
- package/lib/module/index.js +14 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/stores/interstitial-ads-store.js +205 -0
- package/lib/module/stores/interstitial-ads-store.js.map +1 -0
- package/lib/module/stores/native-ads-store.js +204 -0
- package/lib/module/stores/native-ads-store.js.map +1 -0
- package/lib/module/stores/rewarded-ads-store.js +216 -0
- package/lib/module/stores/rewarded-ads-store.js.map +1 -0
- package/lib/module/stores/rewarded-interstitial-ads-store.js +216 -0
- package/lib/module/stores/rewarded-interstitial-ads-store.js.map +1 -0
- package/lib/module/types/ads-types.js +25 -0
- package/lib/module/types/ads-types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/ads-manager/AppOpenAdsManager.d.ts +38 -0
- package/lib/typescript/src/ads-manager/AppOpenAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/ads-manager/GoogleMobileAdsManager.d.ts +22 -0
- package/lib/typescript/src/ads-manager/GoogleMobileAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/ads-manager/InterstitialAdsManager.d.ts +22 -0
- package/lib/typescript/src/ads-manager/InterstitialAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts +59 -0
- package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/ads-manager/RewardedAdsManager.d.ts +25 -0
- package/lib/typescript/src/ads-manager/RewardedAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/ads-manager/RewardedInterstitialAdsManager.d.ts +25 -0
- package/lib/typescript/src/ads-manager/RewardedInterstitialAdsManager.d.ts.map +1 -0
- package/lib/typescript/src/components/BannerAdComponent.d.ts +22 -0
- package/lib/typescript/src/components/BannerAdComponent.d.ts.map +1 -0
- package/lib/typescript/src/components/FullscreenStyles.d.ts +155 -0
- package/lib/typescript/src/components/FullscreenStyles.d.ts.map +1 -0
- package/lib/typescript/src/components/NativeAdComponent.d.ts +24 -0
- package/lib/typescript/src/components/NativeAdComponent.d.ts.map +1 -0
- package/lib/typescript/src/components/NativeAdFullscreenComponent.d.ts +26 -0
- package/lib/typescript/src/components/NativeAdFullscreenComponent.d.ts.map +1 -0
- package/lib/typescript/src/components/NativeStyles.d.ts +139 -0
- package/lib/typescript/src/components/NativeStyles.d.ts.map +1 -0
- package/lib/typescript/src/event/custom-event.d.ts +8 -0
- package/lib/typescript/src/event/custom-event.d.ts.map +1 -0
- package/lib/typescript/src/hook/useAppOpenAd.d.ts +19 -0
- package/lib/typescript/src/hook/useAppOpenAd.d.ts.map +1 -0
- package/lib/typescript/src/hook/useInterstitialAd.d.ts +20 -0
- package/lib/typescript/src/hook/useInterstitialAd.d.ts.map +1 -0
- package/lib/typescript/src/hook/useNativeAd.d.ts +20 -0
- package/lib/typescript/src/hook/useNativeAd.d.ts.map +1 -0
- package/lib/typescript/src/hook/useRewardedAd.d.ts +27 -0
- package/lib/typescript/src/hook/useRewardedAd.d.ts.map +1 -0
- package/lib/typescript/src/hook/useRewardedInterstitialAd.d.ts +27 -0
- package/lib/typescript/src/hook/useRewardedInterstitialAd.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +10 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/stores/interstitial-ads-store.d.ts +36 -0
- package/lib/typescript/src/stores/interstitial-ads-store.d.ts.map +1 -0
- package/lib/typescript/src/stores/native-ads-store.d.ts +36 -0
- package/lib/typescript/src/stores/native-ads-store.d.ts.map +1 -0
- package/lib/typescript/src/stores/rewarded-ads-store.d.ts +39 -0
- package/lib/typescript/src/stores/rewarded-ads-store.d.ts.map +1 -0
- package/lib/typescript/src/stores/rewarded-interstitial-ads-store.d.ts +39 -0
- package/lib/typescript/src/stores/rewarded-interstitial-ads-store.d.ts.map +1 -0
- package/lib/typescript/src/types/ads-types.d.ts +164 -0
- package/lib/typescript/src/types/ads-types.d.ts.map +1 -0
- package/package.json +177 -0
- package/src/ads-manager/AppOpenAdsManager.ts +155 -0
- package/src/ads-manager/GoogleMobileAdsManager.ts +68 -0
- package/src/ads-manager/InterstitialAdsManager.ts +371 -0
- package/src/ads-manager/NativeAdsManager.ts +445 -0
- package/src/ads-manager/RewardedAdsManager.ts +383 -0
- package/src/ads-manager/RewardedInterstitialAdsManager.ts +410 -0
- package/src/components/BannerAdComponent.tsx +205 -0
- package/src/components/FullscreenStyles.ts +158 -0
- package/src/components/NativeAdComponent.tsx +208 -0
- package/src/components/NativeAdFullscreenComponent.tsx +261 -0
- package/src/components/NativeStyles.ts +141 -0
- package/src/event/custom-event.ts +30 -0
- package/src/hook/useAppOpenAd.ts +161 -0
- package/src/hook/useInterstitialAd.ts +195 -0
- package/src/hook/useNativeAd.ts +173 -0
- package/src/hook/useRewardedAd.ts +205 -0
- package/src/hook/useRewardedInterstitialAd.ts +212 -0
- package/src/index.tsx +33 -0
- package/src/stores/interstitial-ads-store.ts +287 -0
- package/src/stores/native-ads-store.ts +272 -0
- package/src/stores/rewarded-ads-store.ts +290 -0
- package/src/stores/rewarded-interstitial-ads-store.ts +318 -0
- package/src/types/ads-types.ts +239 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AdStatus,
|
|
3
|
+
type AdConfig,
|
|
4
|
+
type AdData,
|
|
5
|
+
type AdInstance,
|
|
6
|
+
type AdManagerState,
|
|
7
|
+
} from '../types/ads-types';
|
|
8
|
+
import { googleMobileAdsManager } from './GoogleMobileAdsManager';
|
|
9
|
+
import { CustomEventEmitter } from '../event/custom-event';
|
|
10
|
+
|
|
11
|
+
export class NativeAdsManager extends CustomEventEmitter {
|
|
12
|
+
private state: AdManagerState = {
|
|
13
|
+
ads: new Map(),
|
|
14
|
+
loadingKeys: new Set(),
|
|
15
|
+
displayedKeys: new Set(),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
private configs: Map<string, AdConfig> = new Map();
|
|
19
|
+
private loadPromises: Map<string, Promise<void>> = new Map();
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
// Tự động khởi tạo Google Mobile Ads SDK
|
|
24
|
+
this.initializeGoogleMobileAds();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Khởi tạo Google Mobile Ads SDK
|
|
29
|
+
*/
|
|
30
|
+
private async initializeGoogleMobileAds(): Promise<void> {
|
|
31
|
+
try {
|
|
32
|
+
await googleMobileAdsManager.initialize();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn('Failed to initialize Google Mobile Ads SDK:', error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Preload một native ad với config
|
|
40
|
+
*/
|
|
41
|
+
async preloadAd(config: AdConfig): Promise<void> {
|
|
42
|
+
const fullConfig: AdConfig = {
|
|
43
|
+
...config,
|
|
44
|
+
timeout: config.timeout ?? 10000,
|
|
45
|
+
maxRetries: config.maxRetries ?? 3,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const { adKey, adId } = fullConfig;
|
|
49
|
+
this.configs.set(adKey, fullConfig);
|
|
50
|
+
|
|
51
|
+
// Tạo ad instance nếu chưa tồn tại
|
|
52
|
+
if (!this.state.ads.has(adKey)) {
|
|
53
|
+
const adInstance: AdInstance = {
|
|
54
|
+
adKey,
|
|
55
|
+
adId,
|
|
56
|
+
status: AdStatus.IDLE,
|
|
57
|
+
data: null,
|
|
58
|
+
error: null,
|
|
59
|
+
loadTime: null,
|
|
60
|
+
displayTime: null,
|
|
61
|
+
retryCount: 0,
|
|
62
|
+
isUsingHighPriority: false,
|
|
63
|
+
hasTriedHighPriority: false,
|
|
64
|
+
};
|
|
65
|
+
this.state.ads.set(adKey, adInstance);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Nếu đang load rồi thì return promise hiện tại
|
|
69
|
+
if (this.loadPromises.has(adKey)) {
|
|
70
|
+
return this.loadPromises.get(adKey)!;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Bắt đầu load
|
|
74
|
+
const loadPromise = this.loadAdInternal(adKey);
|
|
75
|
+
this.loadPromises.set(adKey, loadPromise);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await loadPromise;
|
|
79
|
+
} finally {
|
|
80
|
+
this.loadPromises.delete(adKey);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Load ad ngay lập tức (alias của preloadAd)
|
|
86
|
+
*/
|
|
87
|
+
async loadAd(config: AdConfig): Promise<void> {
|
|
88
|
+
return this.preloadAd(config);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Hiển thị ad đã được load
|
|
93
|
+
*/
|
|
94
|
+
async displayAd(adKey: string): Promise<AdInstance | null> {
|
|
95
|
+
const ad = this.state.ads.get(adKey);
|
|
96
|
+
|
|
97
|
+
if (!ad) {
|
|
98
|
+
console.warn(`Ad with key "${adKey}" not found`);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (ad.status === AdStatus.LOADED) {
|
|
103
|
+
ad.status = AdStatus.DISPLAYED;
|
|
104
|
+
ad.displayTime = Date.now();
|
|
105
|
+
this.state.displayedKeys.add(adKey);
|
|
106
|
+
|
|
107
|
+
this.emit('adDisplayed', ad);
|
|
108
|
+
|
|
109
|
+
// const config = this.configs.get(adKey);
|
|
110
|
+
// if (config) {
|
|
111
|
+
// // Preload ad mới trong background (không await để không block)
|
|
112
|
+
// this.preloadAd(adKey, config.adId, config).catch((error) => {
|
|
113
|
+
// console.warn(`Failed to preload new ad for key "${adKey}":`, error);
|
|
114
|
+
// });
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
return ad;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (ad.status === AdStatus.LOADING) {
|
|
121
|
+
// Đợi load xong
|
|
122
|
+
await this.waitForAdLoad(adKey);
|
|
123
|
+
return this.displayAd(adKey);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (ad.status === AdStatus.ERROR) {
|
|
127
|
+
// Thử load lại
|
|
128
|
+
await this.refreshAd(adKey);
|
|
129
|
+
return this.displayAd(adKey);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.warn(
|
|
133
|
+
`Cannot display ad with key "${adKey}" in status: ${ad.status}`
|
|
134
|
+
);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Lấy ad instance
|
|
140
|
+
*/
|
|
141
|
+
getAd(adKey: string): AdInstance | null {
|
|
142
|
+
return this.state.ads.get(adKey) || null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Xóa ad
|
|
147
|
+
*/
|
|
148
|
+
removeAd(adKey: string): void {
|
|
149
|
+
const ad = this.state.ads.get(adKey);
|
|
150
|
+
if (ad?.data?.unsubscribeListeners) {
|
|
151
|
+
ad.data.unsubscribeListeners();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.state.ads.delete(adKey);
|
|
155
|
+
this.state.loadingKeys.delete(adKey);
|
|
156
|
+
this.state.displayedKeys.delete(adKey);
|
|
157
|
+
this.configs.delete(adKey);
|
|
158
|
+
this.loadPromises.delete(adKey);
|
|
159
|
+
this.emit('adRemoved', adKey);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Refresh ad (load lại)
|
|
164
|
+
*/
|
|
165
|
+
async refreshAd(adKey: string): Promise<void> {
|
|
166
|
+
const config = this.configs.get(adKey);
|
|
167
|
+
if (!config) {
|
|
168
|
+
throw new Error(`Config for key "${adKey}" not found`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Reset status
|
|
172
|
+
const ad = this.state.ads.get(adKey);
|
|
173
|
+
if (ad) {
|
|
174
|
+
ad.status = AdStatus.IDLE;
|
|
175
|
+
ad.error = null;
|
|
176
|
+
ad.retryCount = 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return this.preloadAd(config);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Clear tất cả ads
|
|
184
|
+
*/
|
|
185
|
+
clearAllAds(): void {
|
|
186
|
+
const keys = Array.from(this.state.ads.keys());
|
|
187
|
+
keys.forEach((adKey) => this.removeAd(adKey));
|
|
188
|
+
this.emit('allAdsCleared');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Lấy state hiện tại
|
|
193
|
+
*/
|
|
194
|
+
getState(): AdManagerState {
|
|
195
|
+
return {
|
|
196
|
+
ads: new Map(this.state.ads),
|
|
197
|
+
loadingKeys: new Set(this.state.loadingKeys),
|
|
198
|
+
displayedKeys: new Set(this.state.displayedKeys),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Internal method để load ad
|
|
204
|
+
*/
|
|
205
|
+
private async loadAdInternal(adKey: string): Promise<void> {
|
|
206
|
+
const ad = this.state.ads.get(adKey);
|
|
207
|
+
const config = this.configs.get(adKey);
|
|
208
|
+
|
|
209
|
+
if (!ad || !config) {
|
|
210
|
+
throw new Error(`Ad or config for key "${adKey}" not found`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Xác định adId sẽ được sử dụng
|
|
214
|
+
let adIdToLoad = config.adId;
|
|
215
|
+
|
|
216
|
+
// Nếu có adHighpriorityId và chưa thử load high priority
|
|
217
|
+
if (config.adHighpriorityId && !ad.hasTriedHighPriority) {
|
|
218
|
+
adIdToLoad = config.adHighpriorityId;
|
|
219
|
+
ad.isUsingHighPriority = true;
|
|
220
|
+
} else {
|
|
221
|
+
ad.isUsingHighPriority = false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Kiểm tra retry limit
|
|
225
|
+
if (ad.retryCount >= config.maxRetries!) {
|
|
226
|
+
// Nếu đang dùng high priority và đã hết retry, fallback về adId bình thường
|
|
227
|
+
if (ad.isUsingHighPriority) {
|
|
228
|
+
console.log(
|
|
229
|
+
`High priority ad failed after ${config.maxRetries} retries, falling back to normal ad`
|
|
230
|
+
);
|
|
231
|
+
ad.hasTriedHighPriority = true;
|
|
232
|
+
ad.retryCount = 0; // Reset retry count cho adId bình thường
|
|
233
|
+
ad.isUsingHighPriority = false;
|
|
234
|
+
// Không return, tiếp tục load với adId bình thường ở lần recursive call
|
|
235
|
+
return this.loadAdInternal(adKey);
|
|
236
|
+
} else {
|
|
237
|
+
// Nếu đã dùng adId bình thường mà vẫn lỗi
|
|
238
|
+
ad.status = AdStatus.ERROR;
|
|
239
|
+
ad.error = `Max retries (${config.maxRetries}) exceeded`;
|
|
240
|
+
this.emit('adError', ad);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
ad.status = AdStatus.LOADING;
|
|
246
|
+
ad.retryCount++;
|
|
247
|
+
this.state.loadingKeys.add(adKey);
|
|
248
|
+
|
|
249
|
+
this.emit('adLoading', ad);
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
// Gọi native method để load ad
|
|
253
|
+
const adData = await this.loadNativeAd(adIdToLoad, config.timeout!);
|
|
254
|
+
|
|
255
|
+
ad.status = AdStatus.LOADED;
|
|
256
|
+
ad.data = adData;
|
|
257
|
+
ad.error = null;
|
|
258
|
+
ad.loadTime = Date.now();
|
|
259
|
+
ad.retryCount = 0;
|
|
260
|
+
|
|
261
|
+
// Nếu load high priority thành công, đánh dấu đã thử
|
|
262
|
+
if (ad.isUsingHighPriority) {
|
|
263
|
+
ad.hasTriedHighPriority = true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.state.loadingKeys.delete(adKey);
|
|
267
|
+
this.emit('adLoaded', ad);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
ad.status = AdStatus.ERROR;
|
|
270
|
+
ad.error = error instanceof Error ? error.message : String(error);
|
|
271
|
+
this.state.loadingKeys.delete(adKey);
|
|
272
|
+
this.emit('adError', ad);
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Đợi ad load xong
|
|
279
|
+
*/
|
|
280
|
+
private async waitForAdLoad(adKey: string): Promise<void> {
|
|
281
|
+
return new Promise((resolve, reject) => {
|
|
282
|
+
const timeout = setTimeout(() => {
|
|
283
|
+
reject(new Error(`Timeout waiting for ad "${adKey}" to load`));
|
|
284
|
+
}, 10000);
|
|
285
|
+
|
|
286
|
+
const checkAd = () => {
|
|
287
|
+
const ad = this.state.ads.get(adKey);
|
|
288
|
+
if (!ad) {
|
|
289
|
+
clearTimeout(timeout);
|
|
290
|
+
reject(new Error(`Ad "${adKey}" not found`));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (ad.status === AdStatus.LOADED) {
|
|
295
|
+
clearTimeout(timeout);
|
|
296
|
+
resolve();
|
|
297
|
+
} else if (ad.status === AdStatus.ERROR) {
|
|
298
|
+
clearTimeout(timeout);
|
|
299
|
+
reject(new Error(ad.error || 'Unknown error'));
|
|
300
|
+
} else {
|
|
301
|
+
setTimeout(checkAd, 100);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
checkAd();
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private googleMobileAdsModule: any = null;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Load native ad sử dụng react-native-google-mobile-ads
|
|
313
|
+
*/
|
|
314
|
+
private async loadNativeAd(adId: string, timeout: number): Promise<AdData> {
|
|
315
|
+
try {
|
|
316
|
+
// Đảm bảo Google Mobile Ads SDK đã được khởi tạo
|
|
317
|
+
await googleMobileAdsManager.initialize();
|
|
318
|
+
|
|
319
|
+
// Cache import để tránh import nhiều lần
|
|
320
|
+
if (!this.googleMobileAdsModule) {
|
|
321
|
+
this.googleMobileAdsModule = await import(
|
|
322
|
+
'react-native-google-mobile-ads'
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const {
|
|
327
|
+
NativeAd,
|
|
328
|
+
TestIds,
|
|
329
|
+
NativeAdChoicesPlacement,
|
|
330
|
+
NativeAdEventType,
|
|
331
|
+
NativeMediaAspectRatio,
|
|
332
|
+
} = this.googleMobileAdsModule;
|
|
333
|
+
|
|
334
|
+
// Sử dụng test ID nếu adId là test
|
|
335
|
+
const finalAdId = adId === 'test' ? TestIds.NATIVE : adId;
|
|
336
|
+
|
|
337
|
+
// Tạo promise với timeout
|
|
338
|
+
const loadPromise = new Promise<AdData>((resolve, reject) => {
|
|
339
|
+
const timeoutId = setTimeout(() => {
|
|
340
|
+
reject(new Error(`Timeout loading ad after ${timeout}ms`));
|
|
341
|
+
}, timeout);
|
|
342
|
+
|
|
343
|
+
// Tạo native ad request
|
|
344
|
+
NativeAd.createForAdRequest(finalAdId, {
|
|
345
|
+
aspectRatio: NativeMediaAspectRatio.LANDSCAPE,
|
|
346
|
+
adChoicesPlacement: NativeAdChoicesPlacement.TOP_RIGHT,
|
|
347
|
+
startVideoMuted: false,
|
|
348
|
+
})
|
|
349
|
+
.then((nativeAd: InstanceType<typeof NativeAd>) => {
|
|
350
|
+
clearTimeout(timeoutId);
|
|
351
|
+
|
|
352
|
+
console.log('nativeAd', 'loadNativeAd');
|
|
353
|
+
|
|
354
|
+
// Thêm event listeners cho native ad
|
|
355
|
+
const unsubscribeClicked = nativeAd.addAdEventListener(
|
|
356
|
+
NativeAdEventType.CLICKED,
|
|
357
|
+
() => {
|
|
358
|
+
console.log('Native ad clicked');
|
|
359
|
+
this.emit('adClicked', { adId, nativeAd });
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const unsubscribeImpression = nativeAd.addAdEventListener(
|
|
364
|
+
NativeAdEventType.IMPRESSION,
|
|
365
|
+
() => {
|
|
366
|
+
console.log('Native ad impression recorded');
|
|
367
|
+
this.emit('adImpression', { adId, nativeAd });
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
// const unsubscribeOpened = nativeAd.addAdEventListener(
|
|
371
|
+
// AdEventType.OPENED,
|
|
372
|
+
// () => {
|
|
373
|
+
// console.log('Native ad opened');
|
|
374
|
+
// this.emit('adOpened', { adId, nativeAd });
|
|
375
|
+
// }
|
|
376
|
+
// );
|
|
377
|
+
|
|
378
|
+
// const unsubscribeClosed = nativeAd.addAdEventListener(
|
|
379
|
+
// AdEventType.CLOSED,
|
|
380
|
+
// () => {
|
|
381
|
+
// console.log('Native ad closed');
|
|
382
|
+
// this.emit('adClosed', { adId, nativeAd });
|
|
383
|
+
// }
|
|
384
|
+
// );
|
|
385
|
+
|
|
386
|
+
// Chuyển đổi native ad data thành AdData format
|
|
387
|
+
const adData: AdData = {
|
|
388
|
+
id: adId,
|
|
389
|
+
title: nativeAd.headline || '',
|
|
390
|
+
description: nativeAd.body || '',
|
|
391
|
+
imageUrl: nativeAd.images?.[0]?.url || nativeAd.icon?.url || '',
|
|
392
|
+
callToAction: nativeAd.callToAction || 'Learn More',
|
|
393
|
+
advertiser: nativeAd.advertiser || '',
|
|
394
|
+
rating: nativeAd.starRating || undefined,
|
|
395
|
+
price: nativeAd.price || undefined,
|
|
396
|
+
store: nativeAd.store || undefined,
|
|
397
|
+
// Thêm các field khác từ native ad
|
|
398
|
+
nativeAd: nativeAd, // Lưu reference đến native ad object
|
|
399
|
+
iconUrl: nativeAd.icon?.url,
|
|
400
|
+
images:
|
|
401
|
+
nativeAd.images?.map((img: { url: any }) => img.url) || [],
|
|
402
|
+
// Lưu unsubscribe functions để cleanup sau này
|
|
403
|
+
unsubscribeListeners: () => {
|
|
404
|
+
unsubscribeClicked();
|
|
405
|
+
unsubscribeImpression();
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
resolve(adData);
|
|
410
|
+
})
|
|
411
|
+
.catch((error: { message: any }) => {
|
|
412
|
+
clearTimeout(timeoutId);
|
|
413
|
+
reject(new Error(`Failed to load native ad: ${error.message}`));
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
return await loadPromise;
|
|
418
|
+
} catch (error) {
|
|
419
|
+
// Fallback về mock data nếu có lỗi import hoặc load
|
|
420
|
+
console.warn('Failed to load native ad, using mock data:', error);
|
|
421
|
+
|
|
422
|
+
return new Promise((resolve) => {
|
|
423
|
+
setTimeout(
|
|
424
|
+
() => {
|
|
425
|
+
resolve({
|
|
426
|
+
id: adId,
|
|
427
|
+
title: 'Sample Ad Title',
|
|
428
|
+
description: 'This is a sample ad description',
|
|
429
|
+
imageUrl: 'https://via.placeholder.com/300x200',
|
|
430
|
+
callToAction: 'Learn More',
|
|
431
|
+
advertiser: 'Sample Advertiser',
|
|
432
|
+
rating: 4.5,
|
|
433
|
+
price: '$9.99',
|
|
434
|
+
store: 'App Store',
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
1000 + Math.random() * 2000
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Singleton instance
|
|
445
|
+
export const nativeAdsManager = new NativeAdsManager();
|