@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.
Files changed (128) hide show
  1. package/AdsSdk.podspec +52 -0
  2. package/LICENSE +20 -0
  3. package/README.md +95 -0
  4. package/android/build.gradle +108 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +5 -0
  7. package/android/src/main/java/com/adssdk/AdsSdkModule.kt +23 -0
  8. package/android/src/main/java/com/adssdk/AdsSdkPackage.kt +33 -0
  9. package/ios/AdsSdk.h +5 -0
  10. package/ios/AdsSdk.mm +18 -0
  11. package/lib/module/ads-manager/AppOpenAdsManager.js +131 -0
  12. package/lib/module/ads-manager/AppOpenAdsManager.js.map +1 -0
  13. package/lib/module/ads-manager/GoogleMobileAdsManager.js +62 -0
  14. package/lib/module/ads-manager/GoogleMobileAdsManager.js.map +1 -0
  15. package/lib/module/ads-manager/InterstitialAdsManager.js +291 -0
  16. package/lib/module/ads-manager/InterstitialAdsManager.js.map +1 -0
  17. package/lib/module/ads-manager/NativeAdsManager.js +405 -0
  18. package/lib/module/ads-manager/NativeAdsManager.js.map +1 -0
  19. package/lib/module/ads-manager/RewardedAdsManager.js +328 -0
  20. package/lib/module/ads-manager/RewardedAdsManager.js.map +1 -0
  21. package/lib/module/ads-manager/RewardedInterstitialAdsManager.js +328 -0
  22. package/lib/module/ads-manager/RewardedInterstitialAdsManager.js.map +1 -0
  23. package/lib/module/components/BannerAdComponent.js +159 -0
  24. package/lib/module/components/BannerAdComponent.js.map +1 -0
  25. package/lib/module/components/FullscreenStyles.js +162 -0
  26. package/lib/module/components/FullscreenStyles.js.map +1 -0
  27. package/lib/module/components/NativeAdComponent.js +172 -0
  28. package/lib/module/components/NativeAdComponent.js.map +1 -0
  29. package/lib/module/components/NativeAdFullscreenComponent.js +205 -0
  30. package/lib/module/components/NativeAdFullscreenComponent.js.map +1 -0
  31. package/lib/module/components/NativeStyles.js +143 -0
  32. package/lib/module/components/NativeStyles.js.map +1 -0
  33. package/lib/module/event/custom-event.js +29 -0
  34. package/lib/module/event/custom-event.js.map +1 -0
  35. package/lib/module/hook/useAppOpenAd.js +138 -0
  36. package/lib/module/hook/useAppOpenAd.js.map +1 -0
  37. package/lib/module/hook/useInterstitialAd.js +157 -0
  38. package/lib/module/hook/useInterstitialAd.js.map +1 -0
  39. package/lib/module/hook/useNativeAd.js +147 -0
  40. package/lib/module/hook/useNativeAd.js.map +1 -0
  41. package/lib/module/hook/useRewardedAd.js +166 -0
  42. package/lib/module/hook/useRewardedAd.js.map +1 -0
  43. package/lib/module/hook/useRewardedInterstitialAd.js +166 -0
  44. package/lib/module/hook/useRewardedInterstitialAd.js.map +1 -0
  45. package/lib/module/index.js +14 -0
  46. package/lib/module/index.js.map +1 -0
  47. package/lib/module/package.json +1 -0
  48. package/lib/module/stores/interstitial-ads-store.js +205 -0
  49. package/lib/module/stores/interstitial-ads-store.js.map +1 -0
  50. package/lib/module/stores/native-ads-store.js +204 -0
  51. package/lib/module/stores/native-ads-store.js.map +1 -0
  52. package/lib/module/stores/rewarded-ads-store.js +216 -0
  53. package/lib/module/stores/rewarded-ads-store.js.map +1 -0
  54. package/lib/module/stores/rewarded-interstitial-ads-store.js +216 -0
  55. package/lib/module/stores/rewarded-interstitial-ads-store.js.map +1 -0
  56. package/lib/module/types/ads-types.js +25 -0
  57. package/lib/module/types/ads-types.js.map +1 -0
  58. package/lib/typescript/package.json +1 -0
  59. package/lib/typescript/src/ads-manager/AppOpenAdsManager.d.ts +38 -0
  60. package/lib/typescript/src/ads-manager/AppOpenAdsManager.d.ts.map +1 -0
  61. package/lib/typescript/src/ads-manager/GoogleMobileAdsManager.d.ts +22 -0
  62. package/lib/typescript/src/ads-manager/GoogleMobileAdsManager.d.ts.map +1 -0
  63. package/lib/typescript/src/ads-manager/InterstitialAdsManager.d.ts +22 -0
  64. package/lib/typescript/src/ads-manager/InterstitialAdsManager.d.ts.map +1 -0
  65. package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts +59 -0
  66. package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts.map +1 -0
  67. package/lib/typescript/src/ads-manager/RewardedAdsManager.d.ts +25 -0
  68. package/lib/typescript/src/ads-manager/RewardedAdsManager.d.ts.map +1 -0
  69. package/lib/typescript/src/ads-manager/RewardedInterstitialAdsManager.d.ts +25 -0
  70. package/lib/typescript/src/ads-manager/RewardedInterstitialAdsManager.d.ts.map +1 -0
  71. package/lib/typescript/src/components/BannerAdComponent.d.ts +22 -0
  72. package/lib/typescript/src/components/BannerAdComponent.d.ts.map +1 -0
  73. package/lib/typescript/src/components/FullscreenStyles.d.ts +155 -0
  74. package/lib/typescript/src/components/FullscreenStyles.d.ts.map +1 -0
  75. package/lib/typescript/src/components/NativeAdComponent.d.ts +24 -0
  76. package/lib/typescript/src/components/NativeAdComponent.d.ts.map +1 -0
  77. package/lib/typescript/src/components/NativeAdFullscreenComponent.d.ts +26 -0
  78. package/lib/typescript/src/components/NativeAdFullscreenComponent.d.ts.map +1 -0
  79. package/lib/typescript/src/components/NativeStyles.d.ts +139 -0
  80. package/lib/typescript/src/components/NativeStyles.d.ts.map +1 -0
  81. package/lib/typescript/src/event/custom-event.d.ts +8 -0
  82. package/lib/typescript/src/event/custom-event.d.ts.map +1 -0
  83. package/lib/typescript/src/hook/useAppOpenAd.d.ts +19 -0
  84. package/lib/typescript/src/hook/useAppOpenAd.d.ts.map +1 -0
  85. package/lib/typescript/src/hook/useInterstitialAd.d.ts +20 -0
  86. package/lib/typescript/src/hook/useInterstitialAd.d.ts.map +1 -0
  87. package/lib/typescript/src/hook/useNativeAd.d.ts +20 -0
  88. package/lib/typescript/src/hook/useNativeAd.d.ts.map +1 -0
  89. package/lib/typescript/src/hook/useRewardedAd.d.ts +27 -0
  90. package/lib/typescript/src/hook/useRewardedAd.d.ts.map +1 -0
  91. package/lib/typescript/src/hook/useRewardedInterstitialAd.d.ts +27 -0
  92. package/lib/typescript/src/hook/useRewardedInterstitialAd.d.ts.map +1 -0
  93. package/lib/typescript/src/index.d.ts +10 -0
  94. package/lib/typescript/src/index.d.ts.map +1 -0
  95. package/lib/typescript/src/stores/interstitial-ads-store.d.ts +36 -0
  96. package/lib/typescript/src/stores/interstitial-ads-store.d.ts.map +1 -0
  97. package/lib/typescript/src/stores/native-ads-store.d.ts +36 -0
  98. package/lib/typescript/src/stores/native-ads-store.d.ts.map +1 -0
  99. package/lib/typescript/src/stores/rewarded-ads-store.d.ts +39 -0
  100. package/lib/typescript/src/stores/rewarded-ads-store.d.ts.map +1 -0
  101. package/lib/typescript/src/stores/rewarded-interstitial-ads-store.d.ts +39 -0
  102. package/lib/typescript/src/stores/rewarded-interstitial-ads-store.d.ts.map +1 -0
  103. package/lib/typescript/src/types/ads-types.d.ts +164 -0
  104. package/lib/typescript/src/types/ads-types.d.ts.map +1 -0
  105. package/package.json +177 -0
  106. package/src/ads-manager/AppOpenAdsManager.ts +155 -0
  107. package/src/ads-manager/GoogleMobileAdsManager.ts +68 -0
  108. package/src/ads-manager/InterstitialAdsManager.ts +371 -0
  109. package/src/ads-manager/NativeAdsManager.ts +445 -0
  110. package/src/ads-manager/RewardedAdsManager.ts +383 -0
  111. package/src/ads-manager/RewardedInterstitialAdsManager.ts +410 -0
  112. package/src/components/BannerAdComponent.tsx +205 -0
  113. package/src/components/FullscreenStyles.ts +158 -0
  114. package/src/components/NativeAdComponent.tsx +208 -0
  115. package/src/components/NativeAdFullscreenComponent.tsx +261 -0
  116. package/src/components/NativeStyles.ts +141 -0
  117. package/src/event/custom-event.ts +30 -0
  118. package/src/hook/useAppOpenAd.ts +161 -0
  119. package/src/hook/useInterstitialAd.ts +195 -0
  120. package/src/hook/useNativeAd.ts +173 -0
  121. package/src/hook/useRewardedAd.ts +205 -0
  122. package/src/hook/useRewardedInterstitialAd.ts +212 -0
  123. package/src/index.tsx +33 -0
  124. package/src/stores/interstitial-ads-store.ts +287 -0
  125. package/src/stores/native-ads-store.ts +272 -0
  126. package/src/stores/rewarded-ads-store.ts +290 -0
  127. package/src/stores/rewarded-interstitial-ads-store.ts +318 -0
  128. package/src/types/ads-types.ts +239 -0
@@ -0,0 +1,410 @@
1
+ import {
2
+ AdStatus,
3
+ type RewardedInterstitialAdConfig,
4
+ type RewardedInterstitialAdInstance,
5
+ } from '../types/ads-types';
6
+ import { googleMobileAdsManager } from './GoogleMobileAdsManager';
7
+ import { CustomEventEmitter } from '../event/custom-event';
8
+
9
+ export class RewardedInterstitialAdsManager extends CustomEventEmitter {
10
+ private state: Map<string, RewardedInterstitialAdInstance> = new Map();
11
+ private configs: Map<string, RewardedInterstitialAdConfig> = new Map();
12
+ private loadPromises: Map<string, Promise<void>> = new Map();
13
+
14
+ constructor() {
15
+ super();
16
+ this.initializeGoogleMobileAds();
17
+ }
18
+
19
+ private async initializeGoogleMobileAds(): Promise<void> {
20
+ try {
21
+ await googleMobileAdsManager.initialize();
22
+ } catch (error) {
23
+ console.warn('Failed to initialize Google Mobile Ads SDK:', error);
24
+ }
25
+ }
26
+
27
+ // Thay đổi signature: chỉ nhận config
28
+ async preloadAd(config: RewardedInterstitialAdConfig): Promise<void> {
29
+ const fullConfig: RewardedInterstitialAdConfig = {
30
+ ...config,
31
+ timeout: config.timeout ?? 10000,
32
+ maxRetries: config.maxRetries ?? 3,
33
+ showOnAppStateChange: config.showOnAppStateChange ?? false,
34
+ };
35
+
36
+ const { adKey, adId } = fullConfig;
37
+ this.configs.set(adKey, fullConfig);
38
+
39
+ if (!this.state.has(adKey)) {
40
+ const adInstance: RewardedInterstitialAdInstance = {
41
+ adKey,
42
+ adId,
43
+ status: AdStatus.IDLE,
44
+ data: null,
45
+ error: null,
46
+ loadTime: null,
47
+ displayTime: null,
48
+ retryCount: 0,
49
+ type: 'rewarded_interstitial',
50
+ config: fullConfig,
51
+ isUsingHighPriority: false,
52
+ hasTriedHighPriority: false,
53
+ };
54
+ this.state.set(adKey, adInstance);
55
+ }
56
+
57
+ if (this.loadPromises.has(adKey)) {
58
+ return this.loadPromises.get(adKey)!;
59
+ }
60
+
61
+ const loadPromise = this.loadAdInternal(adKey);
62
+ this.loadPromises.set(adKey, loadPromise);
63
+
64
+ try {
65
+ await loadPromise;
66
+ } finally {
67
+ this.loadPromises.delete(adKey);
68
+ }
69
+ }
70
+
71
+ // Thay đổi signature: chỉ nhận config
72
+ async loadAd(config: RewardedInterstitialAdConfig): Promise<void> {
73
+ return this.preloadAd(config);
74
+ }
75
+
76
+ async displayAd(
77
+ adKey: string
78
+ ): Promise<{ ad: RewardedInterstitialAdInstance | null; reward: any }> {
79
+ const ad = this.state.get(adKey);
80
+
81
+ if (!ad) {
82
+ console.warn(`Rewarded interstitial ad with key "${adKey}" not found`);
83
+ return { ad: null, reward: null };
84
+ }
85
+
86
+ if (ad.status === AdStatus.LOADED) {
87
+ try {
88
+ const reward = await this.showRewardedInterstitialAd(ad);
89
+
90
+ ad.status = AdStatus.DISPLAYED;
91
+ ad.displayTime = Date.now();
92
+ ad.reward = reward;
93
+ this.emit('adDisplayed', ad);
94
+
95
+ // Tự động preload lại quảng cáo mới
96
+ // const config = this.configs.get(adKey);
97
+ // if (config) {
98
+ // this.preloadAd(adKey, config.adId, config).catch((error) => {
99
+ // console.warn(
100
+ // `Failed to preload new rewarded interstitial ad for key "${adKey}":`,
101
+ // error
102
+ // );
103
+ // });
104
+ // }
105
+
106
+ return { ad, reward };
107
+ } catch (error) {
108
+ ad.status = AdStatus.ERROR;
109
+ ad.error = error instanceof Error ? error.message : String(error);
110
+ this.emit('adError', ad);
111
+ return { ad: null, reward: null };
112
+ }
113
+ }
114
+
115
+ if (ad.status === AdStatus.LOADING) {
116
+ await this.waitForAdLoad(adKey);
117
+ return this.displayAd(adKey);
118
+ }
119
+
120
+ if (ad.status === AdStatus.ERROR) {
121
+ await this.refreshAd(adKey);
122
+ return this.displayAd(adKey);
123
+ }
124
+
125
+ console.warn(
126
+ `Cannot display rewarded interstitial ad with key "${adKey}" in status: ${ad.status}`
127
+ );
128
+ return { ad: null, reward: null };
129
+ }
130
+
131
+ getAd(adKey: string): RewardedInterstitialAdInstance | null {
132
+ return this.state.get(adKey) || null;
133
+ }
134
+
135
+ removeAd(adKey: string): void {
136
+ this.state.delete(adKey);
137
+ this.configs.delete(adKey);
138
+ this.loadPromises.delete(adKey);
139
+ this.emit('adRemoved', adKey);
140
+ }
141
+
142
+ async refreshAd(adKey: string): Promise<void> {
143
+ const config = this.configs.get(adKey);
144
+ if (!config) {
145
+ throw new Error(`Config for key "${adKey}" not found`);
146
+ }
147
+
148
+ console.log('Refresh ad:', adKey);
149
+
150
+ const ad = this.state.get(adKey);
151
+ if (ad) {
152
+ ad.status = AdStatus.IDLE;
153
+ ad.error = null;
154
+ ad.retryCount = 0;
155
+ }
156
+
157
+ return this.preloadAd(config); // Truyền config thay vì adKey, adId, config
158
+ }
159
+
160
+ clearAllAds(): void {
161
+ const keys = Array.from(this.state.keys());
162
+ keys.forEach((adKey) => this.removeAd(adKey));
163
+ this.emit('allAdsCleared');
164
+ }
165
+
166
+ private async loadAdInternal(adKey: string): Promise<void> {
167
+ const ad = this.state.get(adKey);
168
+ const config = this.configs.get(adKey);
169
+
170
+ if (!ad || !config) {
171
+ throw new Error(`Ad or config for key "${adKey}" not found`);
172
+ }
173
+
174
+ // Xác định adId sẽ được sử dụng
175
+ let adIdToLoad = config.adId;
176
+
177
+ // Nếu có adHighpriorityId và chưa thử load high priority
178
+ if (config.adHighpriorityId && !ad.hasTriedHighPriority) {
179
+ adIdToLoad = config.adHighpriorityId;
180
+ ad.isUsingHighPriority = true;
181
+ } else {
182
+ ad.isUsingHighPriority = false;
183
+ }
184
+
185
+ // Kiểm tra retry limit
186
+ if (ad.retryCount >= config.maxRetries!) {
187
+ // Nếu đang dùng high priority và đã hết retry, fallback về adId bình thường
188
+ if (ad.isUsingHighPriority) {
189
+ console.log(
190
+ `High priority ad failed after ${config.maxRetries} retries, falling back to normal ad`
191
+ );
192
+ ad.hasTriedHighPriority = true;
193
+ ad.retryCount = 0;
194
+ ad.isUsingHighPriority = false;
195
+ return this.loadAdInternal(adKey);
196
+ } else {
197
+ ad.status = AdStatus.ERROR;
198
+ ad.error = `Max retries (${config.maxRetries}) exceeded`;
199
+ this.emit('adError', ad);
200
+ return;
201
+ }
202
+ }
203
+
204
+ ad.status = AdStatus.LOADING;
205
+ ad.retryCount++;
206
+ this.emit('adLoading', ad);
207
+
208
+ try {
209
+ await googleMobileAdsManager.initialize();
210
+
211
+ const rewardedInterstitialAd = await this.loadRewardedInterstitialAd(
212
+ adIdToLoad,
213
+ config.timeout!
214
+ );
215
+
216
+ ad.status = AdStatus.LOADED;
217
+ ad.rewardedInterstitialAd = rewardedInterstitialAd;
218
+ ad.error = null;
219
+ ad.loadTime = Date.now();
220
+ ad.retryCount = 0;
221
+
222
+ if (ad.isUsingHighPriority) {
223
+ ad.hasTriedHighPriority = true;
224
+ }
225
+
226
+ this.emit('adLoaded', ad);
227
+ } catch (error) {
228
+ ad.status = AdStatus.ERROR;
229
+ ad.error = error instanceof Error ? error.message : String(error);
230
+ this.emit('adError', ad);
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ private async loadRewardedInterstitialAd(
236
+ adId: string,
237
+ timeout: number
238
+ ): Promise<any> {
239
+ try {
240
+ const googleMobileAdsModule = await import(
241
+ 'react-native-google-mobile-ads'
242
+ );
243
+ const {
244
+ RewardedInterstitialAd,
245
+ TestIds,
246
+ AdEventType,
247
+ RewardedAdEventType,
248
+ } = googleMobileAdsModule;
249
+
250
+ const finalAdId = adId === 'test' ? TestIds.REWARDED_INTERSTITIAL : adId;
251
+
252
+ return new Promise((resolve, reject) => {
253
+ const timeoutId = setTimeout(() => {
254
+ reject(
255
+ new Error(
256
+ `Timeout loading rewarded interstitial ad after ${timeout}ms`
257
+ )
258
+ );
259
+ }, timeout);
260
+
261
+ // Tạo rewarded interstitial ad instance
262
+ const rewardedInterstitialAd =
263
+ RewardedInterstitialAd.createForAdRequest(finalAdId);
264
+
265
+ // Thêm event listeners
266
+ const unsubscribeLoaded = rewardedInterstitialAd.addAdEventListener(
267
+ RewardedAdEventType.LOADED,
268
+ () => {
269
+ clearTimeout(timeoutId);
270
+ unsubscribeLoaded();
271
+ unsubscribeError();
272
+ resolve(rewardedInterstitialAd);
273
+ }
274
+ );
275
+
276
+ const unsubscribeError = rewardedInterstitialAd.addAdEventListener(
277
+ AdEventType.ERROR,
278
+ (error: any) => {
279
+ clearTimeout(timeoutId);
280
+ unsubscribeLoaded();
281
+ unsubscribeError();
282
+ reject(
283
+ new Error(
284
+ `Failed to load rewarded interstitial ad: ${error.message || error}`
285
+ )
286
+ );
287
+ }
288
+ );
289
+
290
+ // Bắt đầu load quảng cáo
291
+ rewardedInterstitialAd.load();
292
+ });
293
+ } catch (error) {
294
+ console.warn('Failed to load rewarded interstitial ad:', error);
295
+ throw error;
296
+ }
297
+ }
298
+
299
+ private async showRewardedInterstitialAd(
300
+ ad: RewardedInterstitialAdInstance
301
+ ): Promise<any> {
302
+ if (!ad.rewardedInterstitialAd) {
303
+ throw new Error('Rewarded interstitial ad not loaded');
304
+ }
305
+
306
+ return new Promise((resolve, reject) => {
307
+ const googleMobileAdsModule = require('react-native-google-mobile-ads');
308
+ const { AdEventType, RewardedAdEventType } = googleMobileAdsModule;
309
+
310
+ let earnedReward: any = null;
311
+
312
+ // Thêm event listeners cho việc hiển thị
313
+ const unsubscribeEarnedReward =
314
+ ad.rewardedInterstitialAd.addAdEventListener(
315
+ RewardedAdEventType.EARNED_REWARD,
316
+ (reward: any) => {
317
+ earnedReward = reward;
318
+ // Emit sự kiện earnedReward
319
+ this.emit('adEarnedReward', { ad, reward });
320
+ }
321
+ );
322
+
323
+ const unsubscribeClosed = ad.rewardedInterstitialAd.addAdEventListener(
324
+ AdEventType.CLOSED,
325
+ () => {
326
+ unsubscribeEarnedReward();
327
+ unsubscribeClosed();
328
+ unsubscribeError();
329
+ unsubscribeOpened();
330
+ unsubscribeClicked();
331
+
332
+ // Emit sự kiện adClosed
333
+ this.emit('adClosed', ad);
334
+ resolve(earnedReward);
335
+ }
336
+ );
337
+
338
+ const unsubscribeError = ad.rewardedInterstitialAd.addAdEventListener(
339
+ AdEventType.ERROR,
340
+ (error: any) => {
341
+ unsubscribeEarnedReward();
342
+ unsubscribeClosed();
343
+ unsubscribeError();
344
+ unsubscribeOpened();
345
+ unsubscribeClicked();
346
+ reject(
347
+ new Error(
348
+ `Failed to show rewarded interstitial ad: ${error.message || error}`
349
+ )
350
+ );
351
+ }
352
+ );
353
+
354
+ const unsubscribeOpened = ad.rewardedInterstitialAd.addAdEventListener(
355
+ AdEventType.OPENED,
356
+ () => {
357
+ // Emit sự kiện adOpened
358
+ this.emit('adOpened', ad);
359
+ }
360
+ );
361
+
362
+ const unsubscribeClicked = ad.rewardedInterstitialAd.addAdEventListener(
363
+ AdEventType.CLICKED,
364
+ () => {
365
+ // Emit sự kiện adClicked
366
+ this.emit('adClicked', ad);
367
+ }
368
+ );
369
+
370
+ // Hiển thị quảng cáo
371
+ ad.rewardedInterstitialAd.show();
372
+ });
373
+ }
374
+
375
+ private async waitForAdLoad(adKey: string): Promise<void> {
376
+ return new Promise((resolve, reject) => {
377
+ const timeout = setTimeout(() => {
378
+ reject(
379
+ new Error(
380
+ `Timeout waiting for rewarded interstitial ad "${adKey}" to load`
381
+ )
382
+ );
383
+ }, 10000);
384
+
385
+ const checkAd = () => {
386
+ const ad = this.state.get(adKey);
387
+ if (!ad) {
388
+ clearTimeout(timeout);
389
+ reject(new Error(`Rewarded interstitial ad "${adKey}" not found`));
390
+ return;
391
+ }
392
+
393
+ if (ad.status === AdStatus.LOADED) {
394
+ clearTimeout(timeout);
395
+ resolve();
396
+ } else if (ad.status === AdStatus.ERROR) {
397
+ clearTimeout(timeout);
398
+ reject(new Error(ad.error || 'Unknown error'));
399
+ } else {
400
+ setTimeout(checkAd, 100);
401
+ }
402
+ };
403
+
404
+ checkAd();
405
+ });
406
+ }
407
+ }
408
+
409
+ export const rewardedInterstitialAdsManager =
410
+ new RewardedInterstitialAdsManager();
@@ -0,0 +1,205 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { View, StyleSheet, ActivityIndicator, Text } from 'react-native';
3
+ import { BannerAd, type RequestOptions } from 'react-native-google-mobile-ads';
4
+
5
+ interface BannerAdComponentProps {
6
+ adId: string;
7
+ adHighpriorityId?: string;
8
+ style?: any;
9
+ size?:
10
+ | 'BANNER'
11
+ | 'LARGE_BANNER'
12
+ | 'MEDIUM_RECTANGLE'
13
+ | 'FULL_BANNER'
14
+ | 'LEADERBOARD'
15
+ | 'ANCHORED_ADAPTIVE_BANNER';
16
+ onAdLoaded?: () => void;
17
+ onAdFailedToLoad?: (error: Error) => void;
18
+ onAdOpened?: () => void;
19
+ onAdClosed?: () => void;
20
+ onAdClicked?: () => void;
21
+ onAdImpression?: () => void;
22
+ showLoading?: boolean;
23
+ loadingText?: string;
24
+ errorText?: string;
25
+ showError?: boolean;
26
+ requestOptions?: RequestOptions;
27
+ }
28
+
29
+ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
30
+ adId,
31
+ adHighpriorityId,
32
+ style,
33
+ size = 'BANNER',
34
+ onAdLoaded,
35
+ onAdFailedToLoad,
36
+ onAdOpened,
37
+ onAdClosed,
38
+ onAdClicked,
39
+ onAdImpression,
40
+ showLoading = true,
41
+ loadingText = 'Đang tải quảng cáo...',
42
+ errorText = 'Không thể tải quảng cáo',
43
+ showError = true,
44
+ requestOptions,
45
+ }) => {
46
+ const [isLoaded, setIsLoaded] = useState(false);
47
+ const [error, setError] = useState<string | null>(null);
48
+ const [shouldRender, setShouldRender] = useState(false);
49
+ const bannerRef = useRef<any>(null);
50
+
51
+ const [currentAdId, setCurrentAdId] = useState<string>(
52
+ adHighpriorityId || adId
53
+ );
54
+ const [hasTriedHighPriority, setHasTriedHighPriority] =
55
+ useState<boolean>(false);
56
+
57
+ useEffect(() => {
58
+ setShouldRender(true);
59
+ if (adHighpriorityId) {
60
+ setCurrentAdId(adHighpriorityId);
61
+ setHasTriedHighPriority(false);
62
+ }
63
+ }, [adHighpriorityId]);
64
+
65
+ const handleAdLoaded = () => {
66
+ setIsLoaded(true);
67
+ setError(null);
68
+ console.log('PreloadedBannerAd loaded:', currentAdId);
69
+ onAdLoaded?.();
70
+ };
71
+
72
+ const handleAdFailedToLoad = (error: Error) => {
73
+ console.error(
74
+ 'PreloadedBannerAd failed:',
75
+ error.message,
76
+ 'AdId:',
77
+ currentAdId
78
+ );
79
+
80
+ // Nếu đang load high priority và chưa thử fallback, thử lại với adId thường
81
+ if (
82
+ adHighpriorityId &&
83
+ currentAdId === adHighpriorityId &&
84
+ !hasTriedHighPriority
85
+ ) {
86
+ console.log('Fallback to normal adId:', adId);
87
+ setHasTriedHighPriority(true);
88
+ setCurrentAdId(adId);
89
+ setIsLoaded(false);
90
+ setError(null);
91
+ return; // Không gọi onAdFailedToLoad vì sẽ thử lại
92
+ }
93
+
94
+ // Nếu đã thử cả 2 hoặc không có high priority, hiển thị error
95
+ setError(error.message);
96
+ onAdFailedToLoad?.(error);
97
+ };
98
+
99
+ const handleAdOpened = () => {
100
+ console.log('PreloadedBannerAd opened');
101
+ onAdOpened?.();
102
+ };
103
+
104
+ const handleAdClosed = () => {
105
+ console.log('PreloadedBannerAd closed');
106
+ onAdClosed?.();
107
+ };
108
+
109
+ const handleAdClicked = () => {
110
+ console.log('PreloadedBannerAd clicked');
111
+ onAdClicked?.();
112
+ };
113
+
114
+ const handleAdImpression = () => {
115
+ console.log('PreloadedBannerAd impression');
116
+ onAdImpression?.();
117
+ };
118
+
119
+ if (!shouldRender) {
120
+ return null;
121
+ }
122
+
123
+ return (
124
+ <View style={[styles.container, style]}>
125
+ {/* Hiển thị loading chỉ khi chưa load được và chưa có lỗi */}
126
+ {!isLoaded && !error && showLoading && (
127
+ <View style={styles.loadingContainer}>
128
+ <ActivityIndicator size="small" color="#007AFF" />
129
+ <Text style={styles.loadingText}>{loadingText}</Text>
130
+ </View>
131
+ )}
132
+
133
+ {/* Hiển thị error nếu có */}
134
+ {error && showError && (
135
+ <View style={styles.errorContainer}>
136
+ <Text style={styles.errorText}>{errorText}</Text>
137
+ </View>
138
+ )}
139
+
140
+ {/* Banner component - luôn render để load ngay */}
141
+ <BannerAd
142
+ key={currentAdId} // Key quan trọng để force re-render khi adId thay đổi
143
+ ref={bannerRef}
144
+ unitId={currentAdId}
145
+ size={size}
146
+ onAdLoaded={handleAdLoaded}
147
+ onAdFailedToLoad={handleAdFailedToLoad}
148
+ onAdOpened={handleAdOpened}
149
+ onAdClosed={handleAdClosed}
150
+ onAdClicked={handleAdClicked}
151
+ onAdImpression={handleAdImpression}
152
+ requestOptions={requestOptions}
153
+ />
154
+ </View>
155
+ );
156
+ };
157
+
158
+ const styles = StyleSheet.create({
159
+ container: {
160
+ alignItems: 'center',
161
+ justifyContent: 'center',
162
+ minHeight: 50,
163
+ backgroundColor: 'transparent',
164
+ },
165
+ loadingContainer: {
166
+ width: '80%',
167
+ position: 'absolute',
168
+ alignItems: 'center',
169
+ justifyContent: 'center',
170
+ zIndex: 1,
171
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
172
+ borderRadius: 8,
173
+ padding: 12,
174
+ },
175
+ loadingText: {
176
+ marginTop: 8,
177
+ fontSize: 12,
178
+ color: '#666',
179
+ textAlign: 'center',
180
+ },
181
+ errorContainer: {
182
+ alignItems: 'center',
183
+ justifyContent: 'center',
184
+ padding: 10,
185
+ backgroundColor: '#ffe6e6',
186
+ borderRadius: 8,
187
+ borderWidth: 1,
188
+ borderColor: '#ffcccb',
189
+ },
190
+ errorText: {
191
+ fontSize: 12,
192
+ color: '#ff3b30',
193
+ textAlign: 'center',
194
+ },
195
+ banner: {
196
+ width: '100%',
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ },
200
+ hiddenBanner: {
201
+ opacity: 0,
202
+ height: 0,
203
+ overflow: 'hidden',
204
+ },
205
+ });