@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,141 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const styles = StyleSheet.create({
4
+ wrapper: {
5
+ width: '100%',
6
+ alignItems: 'center',
7
+ },
8
+ container: {
9
+ width: '100%',
10
+ backgroundColor: '#FFFFFF',
11
+ borderRadius: 8,
12
+ overflow: 'hidden',
13
+ borderWidth: 1,
14
+ // alignItems: 'center',
15
+ borderColor: '#E5E5E5',
16
+ },
17
+ centerContent: {
18
+ justifyContent: 'center',
19
+ alignItems: 'center',
20
+ padding: 16,
21
+ minHeight: 120,
22
+ },
23
+ loadingText: {
24
+ marginTop: 8,
25
+ fontSize: 13,
26
+ color: '#666',
27
+ },
28
+ errorText: {
29
+ fontSize: 13,
30
+ color: '#FF3B30',
31
+ textAlign: 'center',
32
+ marginBottom: 10,
33
+ },
34
+ retryButton: {
35
+ backgroundColor: '#007AFF',
36
+ paddingHorizontal: 16,
37
+ paddingVertical: 8,
38
+ borderRadius: 6,
39
+ },
40
+ retryText: {
41
+ color: '#FFFFFF',
42
+ fontSize: 13,
43
+ fontWeight: '600',
44
+ },
45
+ emptyText: {
46
+ fontSize: 13,
47
+ color: '#999',
48
+ },
49
+ adHeader: {
50
+ flexDirection: 'row',
51
+ alignItems: 'center',
52
+ justifyContent: 'space-between',
53
+ paddingHorizontal: 12,
54
+ paddingVertical: 8,
55
+ backgroundColor: '#F9F9F9',
56
+ },
57
+ adBadge: {
58
+ backgroundColor: '#FFCC00',
59
+ paddingHorizontal: 6,
60
+ paddingVertical: 2,
61
+ borderRadius: 3,
62
+ fontSize: 10,
63
+ fontWeight: '700',
64
+ color: '#666',
65
+ },
66
+ advertiserText: {
67
+ fontSize: 11,
68
+ color: '#666',
69
+ fontWeight: '500',
70
+ flex: 1,
71
+ marginLeft: 8,
72
+ textAlign: 'right',
73
+ },
74
+ mediaView: {
75
+ minHeight: 180,
76
+ maxHeight: 250,
77
+ alignSelf: 'center',
78
+ backgroundColor: '#F0F0F0',
79
+ },
80
+ contentContainer: {
81
+ padding: 12,
82
+ },
83
+ headline: {
84
+ fontSize: 16,
85
+ fontWeight: '700',
86
+ color: '#000',
87
+ marginBottom: 6,
88
+ lineHeight: 22,
89
+ },
90
+ bodyText: {
91
+ fontSize: 13,
92
+ color: '#666',
93
+ lineHeight: 18,
94
+ marginBottom: 10,
95
+ },
96
+ infoRow: {
97
+ flexDirection: 'row',
98
+ alignItems: 'center',
99
+ marginBottom: 10,
100
+ flexWrap: 'wrap',
101
+ gap: 8,
102
+ },
103
+ ratingText: {
104
+ fontSize: 12,
105
+ fontWeight: '600',
106
+ color: '#FF9500',
107
+ },
108
+ priceText: {
109
+ fontSize: 14,
110
+ fontWeight: '700',
111
+ color: '#34C759',
112
+ },
113
+ storeText: {
114
+ fontSize: 11,
115
+ color: '#999',
116
+ flex: 1,
117
+ },
118
+ ctaButton: {
119
+ backgroundColor: '#007AFF',
120
+ paddingVertical: 10,
121
+ paddingHorizontal: 20,
122
+ borderRadius: 6,
123
+ alignItems: 'center',
124
+ justifyContent: 'center',
125
+ },
126
+ ctaText: {
127
+ fontSize: 14,
128
+ fontWeight: '700',
129
+ color: '#FFFFFF',
130
+ },
131
+ iconContainer: {
132
+ position: 'absolute',
133
+ top: 8,
134
+ right: 8,
135
+ width: 36,
136
+ height: 36,
137
+ borderRadius: 18,
138
+ overflow: 'hidden',
139
+ backgroundColor: '#F0F0F0',
140
+ },
141
+ });
@@ -0,0 +1,30 @@
1
+ export class CustomEventEmitter {
2
+ private events: { [key: string]: Function[] } = {};
3
+
4
+ on(event: string, listener: Function) {
5
+ if (!this.events[event]) {
6
+ this.events[event] = [];
7
+ }
8
+ this.events[event].push(listener);
9
+ }
10
+
11
+ emit(event: string, ...args: any[]) {
12
+ if (this.events[event]) {
13
+ this.events[event].forEach((listener) => listener(...args));
14
+ }
15
+ }
16
+
17
+ off(event: string, listener: Function) {
18
+ if (this.events[event]) {
19
+ this.events[event] = this.events[event].filter((l) => l !== listener);
20
+ }
21
+ }
22
+
23
+ removeAllListeners(event?: string) {
24
+ if (event) {
25
+ delete this.events[event];
26
+ } else {
27
+ this.events = {};
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,161 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { AppState, type AppStateStatus } from 'react-native';
3
+ import { appOpenAdsManager } from '../ads-manager/AppOpenAdsManager';
4
+
5
+ interface UseAppOpenAdOptions {
6
+ adId: string;
7
+ showOnAppForeground?: boolean; // Tự động hiển thị khi app về foreground
8
+ autoLoad?: boolean; // Tự động load ad khi hook mount
9
+ onAdLoaded?: () => void;
10
+ onAdFailedToLoad?: (error: Error) => void;
11
+ onAdOpened?: () => void;
12
+ onAdClosed?: () => void;
13
+ onAdFailedToShow?: (error: Error) => void;
14
+ }
15
+
16
+ export const useAppOpenAd = (options: UseAppOpenAdOptions) => {
17
+ const {
18
+ adId,
19
+ showOnAppForeground = true,
20
+ autoLoad = true,
21
+ onAdLoaded,
22
+ onAdFailedToLoad,
23
+ onAdOpened,
24
+ onAdClosed,
25
+ onAdFailedToShow,
26
+ } = options;
27
+
28
+ const [isLoading, setIsLoading] = useState(false);
29
+ const [isReady, setIsReady] = useState(false);
30
+ const [error, setError] = useState<Error | null>(null);
31
+ const appStateRef = useRef(AppState.currentState);
32
+ const isFirstLaunch = useRef(true);
33
+
34
+ // Sử dụng useRef để lưu callbacks, tránh re-render
35
+ const callbacksRef = useRef({
36
+ onAdLoaded,
37
+ onAdFailedToLoad,
38
+ onAdOpened,
39
+ onAdClosed,
40
+ onAdFailedToShow,
41
+ });
42
+
43
+ // Update callbacks ref khi có thay đổi
44
+ useEffect(() => {
45
+ callbacksRef.current = {
46
+ onAdLoaded,
47
+ onAdFailedToLoad,
48
+ onAdOpened,
49
+ onAdClosed,
50
+ onAdFailedToShow,
51
+ };
52
+ }, [onAdLoaded, onAdFailedToLoad, onAdOpened, onAdClosed, onAdFailedToShow]);
53
+
54
+ // Load ad function - chỉ phụ thuộc vào adId
55
+ const loadAd = useCallback(async () => {
56
+ try {
57
+ setIsLoading(true);
58
+ setError(null);
59
+
60
+ await appOpenAdsManager.loadAd(
61
+ { adId },
62
+ {
63
+ onAdLoaded: () => {
64
+ setIsLoading(false);
65
+ setIsReady(true);
66
+ callbacksRef.current.onAdLoaded?.();
67
+ },
68
+ onAdFailedToLoad: (err) => {
69
+ setIsLoading(false);
70
+ setIsReady(false);
71
+ setError(err);
72
+ callbacksRef.current.onAdFailedToLoad?.(err);
73
+ },
74
+ onAdOpened: () => {
75
+ callbacksRef.current.onAdOpened?.();
76
+ },
77
+ onAdClosed: () => {
78
+ setIsReady(false);
79
+ callbacksRef.current.onAdClosed?.();
80
+ },
81
+ onAdFailedToShow: (err) => {
82
+ setError(err);
83
+ setIsReady(false);
84
+ callbacksRef.current.onAdFailedToShow?.(err);
85
+ },
86
+ }
87
+ );
88
+ } catch (err) {
89
+ setIsLoading(false);
90
+ setIsReady(false);
91
+ setError(err as Error);
92
+ }
93
+ }, [adId]); // Chỉ phụ thuộc vào adId
94
+
95
+ // Show ad function
96
+ const showAd = useCallback(async () => {
97
+ const success = await appOpenAdsManager.showAd();
98
+ if (!success) {
99
+ setIsReady(false);
100
+ }
101
+ return success;
102
+ }, []);
103
+
104
+ // Auto load on mount
105
+ useEffect(() => {
106
+ if (autoLoad) {
107
+ loadAd();
108
+ }
109
+
110
+ return () => {
111
+ // Cleanup when unmount
112
+ appOpenAdsManager.destroy();
113
+ };
114
+ }, [autoLoad, loadAd]);
115
+
116
+ // Handle app state changes
117
+ useEffect(() => {
118
+ if (!showOnAppForeground) {
119
+ return;
120
+ }
121
+
122
+ const subscription = AppState.addEventListener(
123
+ 'change',
124
+ async (nextAppState: AppStateStatus) => {
125
+ // Skip first launch
126
+ if (isFirstLaunch.current) {
127
+ isFirstLaunch.current = false;
128
+ appStateRef.current = nextAppState;
129
+ return;
130
+ }
131
+
132
+ // App comes to foreground
133
+ if (
134
+ appStateRef.current?.match(/background/) &&
135
+ nextAppState === 'active'
136
+ ) {
137
+ if (appOpenAdsManager.isAdAvailable()) {
138
+ await showAd();
139
+ } else {
140
+ // Load new ad if not available
141
+ await loadAd();
142
+ }
143
+ }
144
+
145
+ appStateRef.current = nextAppState;
146
+ }
147
+ );
148
+
149
+ return () => {
150
+ subscription.remove();
151
+ };
152
+ }, [showOnAppForeground, loadAd, showAd]);
153
+
154
+ return {
155
+ isLoading,
156
+ isReady,
157
+ error,
158
+ loadAd,
159
+ showAd,
160
+ };
161
+ };
@@ -0,0 +1,195 @@
1
+ import { useEffect, useCallback } from 'react';
2
+ import { useInterstitialAdStore } from '../stores/interstitial-ads-store';
3
+ import {
4
+ AdStatus,
5
+ type UseInterstitialAdOptions,
6
+ type InterstitialAdConfig,
7
+ } from '../types/ads-types';
8
+ import { interstitialAdsManager } from '../ads-manager/InterstitialAdsManager';
9
+
10
+ // Merge InterstitialAdConfig với UseInterstitialAdOptions
11
+ type UseInterstitialAdConfig = InterstitialAdConfig & UseInterstitialAdOptions;
12
+
13
+ export const useInterstitialAd = (config: UseInterstitialAdConfig) => {
14
+ const {
15
+ adKey,
16
+ adId,
17
+ timeout,
18
+ maxRetries,
19
+ adHighpriorityId,
20
+ showOnAppStateChange,
21
+ // UseInterstitialAdOptions fields
22
+ autoLoad = false,
23
+ onLoad,
24
+ onDisplay,
25
+ onError,
26
+ onClosed,
27
+ onOpened,
28
+ onClicked,
29
+ } = config;
30
+
31
+ // Zustand selectors
32
+ const ad = useInterstitialAdStore((state) => state.getAd(adKey));
33
+ const isLoading = useInterstitialAdStore((state) => state.isLoading(adKey));
34
+ const error = useInterstitialAdStore((state) => state.getError(adKey));
35
+ const isUsingHighPriority = useInterstitialAdStore((state) =>
36
+ state.isUsingHighPriority(adKey)
37
+ );
38
+ const hasTriedHighPriority = useInterstitialAdStore((state) =>
39
+ state.hasTriedHighPriority(adKey)
40
+ );
41
+
42
+ // Zustand actions
43
+ const preloadAd = useInterstitialAdStore((state) => state.preloadAd);
44
+ const loadAd = useInterstitialAdStore((state) => state.loadAd);
45
+ const displayAd = useInterstitialAdStore((state) => state.displayAd);
46
+ const removeAd = useInterstitialAdStore((state) => state.removeAd);
47
+ const refreshAd = useInterstitialAdStore((state) => state.refreshAd);
48
+
49
+ // Memoized callbacks
50
+ const handleLoadAd = useCallback(
51
+ async (customConfig?: Partial<InterstitialAdConfig>) => {
52
+ const finalConfig: InterstitialAdConfig = {
53
+ adKey,
54
+ adId,
55
+ showOnAppStateChange,
56
+ timeout,
57
+ maxRetries,
58
+ adHighpriorityId,
59
+ ...customConfig,
60
+ };
61
+ await loadAd(finalConfig);
62
+ },
63
+ [
64
+ adKey,
65
+ adId,
66
+ loadAd,
67
+ showOnAppStateChange,
68
+ timeout,
69
+ maxRetries,
70
+ adHighpriorityId,
71
+ ]
72
+ );
73
+
74
+ const handlePreloadAd = useCallback(
75
+ async (customConfig?: Partial<InterstitialAdConfig>) => {
76
+ const finalConfig: InterstitialAdConfig = {
77
+ adKey,
78
+ adId,
79
+ showOnAppStateChange,
80
+ timeout,
81
+ maxRetries,
82
+ adHighpriorityId,
83
+ ...customConfig,
84
+ };
85
+ await preloadAd(finalConfig);
86
+ },
87
+ [
88
+ adKey,
89
+ adId,
90
+ preloadAd,
91
+ showOnAppStateChange,
92
+ timeout,
93
+ maxRetries,
94
+ adHighpriorityId,
95
+ ]
96
+ );
97
+
98
+ const handleDisplayAd = useCallback(async () => {
99
+ const displayedAd = await displayAd(adKey);
100
+ if (displayedAd && onDisplay) {
101
+ onDisplay(displayedAd);
102
+ }
103
+ return displayedAd;
104
+ }, [adKey, displayAd, onDisplay]);
105
+
106
+ const handleRemoveAd = useCallback(() => {
107
+ removeAd(adKey);
108
+ }, [adKey, removeAd]);
109
+
110
+ const handleRefreshAd = useCallback(async () => {
111
+ await refreshAd(adKey);
112
+ }, [adKey, refreshAd]);
113
+
114
+ // Auto load effect
115
+ useEffect(() => {
116
+ if (autoLoad && !ad) {
117
+ handleLoadAd();
118
+ }
119
+ }, [autoLoad, ad, handleLoadAd]);
120
+
121
+ // Event callbacks
122
+ useEffect(() => {
123
+ if (ad?.status === AdStatus.LOADED && onLoad) {
124
+ onLoad(ad);
125
+ }
126
+ }, [ad?.status, ad, onLoad]);
127
+
128
+ useEffect(() => {
129
+ if (ad?.status === AdStatus.DISPLAYED && onDisplay) {
130
+ onDisplay(ad);
131
+ }
132
+ }, [ad?.status, ad, onDisplay]);
133
+
134
+ useEffect(() => {
135
+ if (ad?.status === AdStatus.ERROR && ad.error && onError) {
136
+ onError(ad.error, ad);
137
+ }
138
+ }, [ad?.status, ad, onError]);
139
+
140
+ // Thêm các event listeners mới cho các sự kiện từ Google Mobile Ads
141
+ useEffect(() => {
142
+ if (!ad) return;
143
+
144
+ // Lắng nghe sự kiện từ InterstitialAdsManager
145
+ const handleAdOpened = (openedAd: any) => {
146
+ if (openedAd.adKey === adKey && onOpened) {
147
+ onOpened(openedAd);
148
+ }
149
+ };
150
+
151
+ const handleAdClicked = (clickedAd: any) => {
152
+ if (clickedAd.adKey === adKey && onClicked) {
153
+ onClicked(clickedAd);
154
+ }
155
+ };
156
+
157
+ const handleAdClosed = (closedAd: any) => {
158
+ if (closedAd.adKey === adKey && onClosed) {
159
+ onClosed(closedAd);
160
+ }
161
+ };
162
+
163
+ // Đăng ký event listeners
164
+ interstitialAdsManager.on('adOpened', handleAdOpened);
165
+ interstitialAdsManager.on('adClicked', handleAdClicked);
166
+ interstitialAdsManager.on('adClosed', handleAdClosed);
167
+
168
+ // Cleanup listeners khi component unmount
169
+ return () => {
170
+ interstitialAdsManager.off('adOpened', handleAdOpened);
171
+ interstitialAdsManager.off('adClicked', handleAdClicked);
172
+ interstitialAdsManager.off('adClosed', handleAdClosed);
173
+ };
174
+ }, [adKey, onOpened, onClicked, onClosed, ad]);
175
+
176
+ return {
177
+ // State
178
+ ad,
179
+ isLoading,
180
+ error,
181
+ status: ad?.status || AdStatus.IDLE,
182
+ isLoaded: ad?.status === AdStatus.LOADED,
183
+ isDisplayed: ad?.status === AdStatus.DISPLAYED,
184
+ hasError: ad?.status === AdStatus.ERROR,
185
+ isUsingHighPriority,
186
+ hasTriedHighPriority,
187
+
188
+ // Actions
189
+ loadAd: handleLoadAd,
190
+ preloadAd: handlePreloadAd,
191
+ displayAd: handleDisplayAd,
192
+ removeAd: handleRemoveAd,
193
+ refreshAd: handleRefreshAd,
194
+ };
195
+ };
@@ -0,0 +1,173 @@
1
+ import { useEffect, useCallback } from 'react';
2
+ import { useNativeAdStore } from '../stores/native-ads-store';
3
+ import {
4
+ AdStatus,
5
+ type UseNativeAdOptions,
6
+ type AdConfig,
7
+ } from '../types/ads-types';
8
+ import { nativeAdsManager } from '../ads-manager/NativeAdsManager';
9
+
10
+ // Merge AdConfig với UseNativeAdOptions
11
+ type UseNativeAdConfig = AdConfig & UseNativeAdOptions;
12
+
13
+ export const useNativeAd = (config: UseNativeAdConfig) => {
14
+ const {
15
+ adKey,
16
+ adId,
17
+ timeout,
18
+ maxRetries,
19
+ adHighpriorityId,
20
+ // UseNativeAdOptions fields
21
+ autoLoad = false,
22
+ onLoad,
23
+ onDisplay,
24
+ onError,
25
+ onClicked,
26
+ onImpression,
27
+ } = config;
28
+
29
+ // Zustand selectors
30
+ const ad = useNativeAdStore((state) => state.getAd(adKey));
31
+ const isLoading = useNativeAdStore((state) => state.isLoading(adKey));
32
+ const error = useNativeAdStore((state) => state.getError(adKey));
33
+ const isUsingHighPriority = useNativeAdStore((state) =>
34
+ state.isUsingHighPriority(adKey)
35
+ );
36
+ const hasTriedHighPriority = useNativeAdStore((state) =>
37
+ state.hasTriedHighPriority(adKey)
38
+ );
39
+
40
+ // Zustand actions
41
+ const preloadAd = useNativeAdStore((state) => state.preloadAd);
42
+ const loadAd = useNativeAdStore((state) => state.loadAd);
43
+ const displayAd = useNativeAdStore((state) => state.displayAd);
44
+ const removeAd = useNativeAdStore((state) => state.removeAd);
45
+ const refreshAd = useNativeAdStore((state) => state.refreshAd);
46
+
47
+ // Memoized callbacks
48
+ const handleLoadAd = useCallback(
49
+ async (customConfig?: Partial<AdConfig>) => {
50
+ const finalConfig: AdConfig = {
51
+ adKey,
52
+ adId,
53
+ timeout,
54
+ maxRetries,
55
+ adHighpriorityId,
56
+ ...customConfig,
57
+ };
58
+ await loadAd(finalConfig);
59
+ },
60
+ [adKey, adId, timeout, maxRetries, adHighpriorityId, loadAd]
61
+ );
62
+
63
+ const handlePreloadAd = useCallback(
64
+ async (customConfig?: Partial<AdConfig>) => {
65
+ const finalConfig: AdConfig = {
66
+ adKey,
67
+ adId,
68
+ timeout,
69
+ maxRetries,
70
+ adHighpriorityId,
71
+ ...customConfig,
72
+ };
73
+ await preloadAd(finalConfig);
74
+ },
75
+ [adKey, adId, timeout, maxRetries, adHighpriorityId, preloadAd]
76
+ );
77
+
78
+ const handleDisplayAd = useCallback(async () => {
79
+ const displayedAd = await displayAd(adKey);
80
+ if (displayedAd && onDisplay) {
81
+ onDisplay(displayedAd);
82
+ }
83
+ return displayedAd;
84
+ }, [adKey, displayAd, onDisplay]);
85
+
86
+ const handleRemoveAd = useCallback(() => {
87
+ removeAd(adKey);
88
+ }, [adKey, removeAd]);
89
+
90
+ const handleRefreshAd = useCallback(async () => {
91
+ await refreshAd(adKey);
92
+ }, [adKey, refreshAd]);
93
+
94
+ // Auto load effect
95
+ useEffect(() => {
96
+ if (autoLoad && !ad) {
97
+ handleLoadAd();
98
+ }
99
+ }, [autoLoad, ad, handleLoadAd]);
100
+
101
+ // Event callbacks
102
+ useEffect(() => {
103
+ if (ad?.status === AdStatus.LOADED && onLoad) {
104
+ onLoad(ad);
105
+ }
106
+ }, [ad?.status, ad, onLoad]);
107
+
108
+ useEffect(() => {
109
+ if (ad?.status === AdStatus.DISPLAYED && onDisplay) {
110
+ onDisplay(ad);
111
+ }
112
+ }, [ad?.status, ad, onDisplay]);
113
+
114
+ useEffect(() => {
115
+ if (ad?.status === AdStatus.ERROR && ad.error && onError) {
116
+ onError(ad.error, ad);
117
+ }
118
+ }, [ad?.status, ad, onError]);
119
+
120
+ // Click event listener
121
+ useEffect(() => {
122
+ if (!ad || !onClicked) return;
123
+
124
+ const handleClick = (data: any) => {
125
+ if (data.adId === ad.adId) {
126
+ onClicked(ad);
127
+ }
128
+ };
129
+
130
+ nativeAdsManager.on('adClicked', handleClick);
131
+
132
+ return () => {
133
+ nativeAdsManager.off('adClicked', handleClick);
134
+ };
135
+ }, [ad, onClicked]);
136
+
137
+ // Impression event listener
138
+ useEffect(() => {
139
+ if (!ad || !onImpression) return;
140
+
141
+ const handleImpression = (data: any) => {
142
+ if (data.adId === ad.adId) {
143
+ onImpression(ad);
144
+ }
145
+ };
146
+
147
+ nativeAdsManager.on('adImpression', handleImpression);
148
+
149
+ return () => {
150
+ nativeAdsManager.off('adImpression', handleImpression);
151
+ };
152
+ }, [ad, onImpression]);
153
+
154
+ return {
155
+ // State
156
+ ad,
157
+ isLoading,
158
+ error,
159
+ status: ad?.status || AdStatus.IDLE,
160
+ isLoaded: ad?.status === AdStatus.LOADED,
161
+ isDisplayed: ad?.status === AdStatus.DISPLAYED,
162
+ hasError: ad?.status === AdStatus.ERROR,
163
+ isUsingHighPriority,
164
+ hasTriedHighPriority,
165
+
166
+ // Actions
167
+ loadAd: handleLoadAd,
168
+ preloadAd: handlePreloadAd,
169
+ displayAd: handleDisplayAd,
170
+ removeAd: handleRemoveAd,
171
+ refreshAd: handleRefreshAd,
172
+ };
173
+ };