@vynix-org/react-native-ads-sdk 0.2.2 → 0.2.4

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 (29) hide show
  1. package/lib/module/ads-manager/NativeAdsManager.js +26 -8
  2. package/lib/module/ads-manager/NativeAdsManager.js.map +1 -1
  3. package/lib/module/components/NativeAdComponent.js +73 -68
  4. package/lib/module/components/NativeAdComponent.js.map +1 -1
  5. package/lib/module/components/NativeStyles.js +20 -0
  6. package/lib/module/components/NativeStyles.js.map +1 -1
  7. package/lib/module/hook/useNativeAd.js +32 -3
  8. package/lib/module/hook/useNativeAd.js.map +1 -1
  9. package/lib/module/stores/native-ads-store.js +35 -4
  10. package/lib/module/stores/native-ads-store.js.map +1 -1
  11. package/lib/module/types/ads-types.js +1 -0
  12. package/lib/module/types/ads-types.js.map +1 -1
  13. package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts.map +1 -1
  14. package/lib/typescript/src/components/NativeAdComponent.d.ts.map +1 -1
  15. package/lib/typescript/src/components/NativeStyles.d.ts +24 -0
  16. package/lib/typescript/src/components/NativeStyles.d.ts.map +1 -1
  17. package/lib/typescript/src/hook/useNativeAd.d.ts +1 -0
  18. package/lib/typescript/src/hook/useNativeAd.d.ts.map +1 -1
  19. package/lib/typescript/src/stores/native-ads-store.d.ts +3 -0
  20. package/lib/typescript/src/stores/native-ads-store.d.ts.map +1 -1
  21. package/lib/typescript/src/types/ads-types.d.ts +4 -0
  22. package/lib/typescript/src/types/ads-types.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/ads-manager/NativeAdsManager.ts +32 -8
  25. package/src/components/NativeAdComponent.tsx +84 -70
  26. package/src/components/NativeStyles.ts +20 -0
  27. package/src/hook/useNativeAd.ts +34 -5
  28. package/src/stores/native-ads-store.ts +37 -2
  29. package/src/types/ads-types.ts +4 -0
@@ -52,7 +52,7 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
52
52
  headlineStyles,
53
53
  bodyStyles,
54
54
  }) => {
55
- const { ad, isLoading, error, isLoaded } = useNativeAd({
55
+ const { ad, isLoading, error, isLoaded, isRefreshing } = useNativeAd({
56
56
  adKey,
57
57
  adId,
58
58
  autoLoad,
@@ -63,8 +63,15 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
63
63
  onImpression,
64
64
  });
65
65
 
66
- // Render loading state
67
- if (isLoading) {
66
+ // useEffect(() => {
67
+ // loadAd();
68
+ // }, [loadAd]);
69
+
70
+ const hasNativeAd = Boolean(ad?.data?.nativeAd);
71
+ const shouldShowSkeleton = !hasNativeAd && isLoading;
72
+
73
+ // Render loading state (only when chưa có dữ liệu cũ)
74
+ if (shouldShowSkeleton) {
68
75
  return (
69
76
  <View style={[styles.wrapper, style]}>
70
77
  <View style={[styles.container, styles.centerContent]}>
@@ -75,90 +82,97 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
75
82
  );
76
83
  }
77
84
 
78
- // Render error state
79
- if (error) {
80
- return null;
81
- }
82
-
83
85
  // Render empty state
84
- if (!isLoaded || !ad?.data?.nativeAd) {
86
+ if (!ad || !isLoaded || !hasNativeAd || error) {
85
87
  return null;
86
88
  }
87
89
 
88
- const nativeAd = ad.data.nativeAd as NativeAd;
90
+ const nativeAd = ad.data?.nativeAd as NativeAd;
89
91
 
90
92
  return (
91
93
  <View style={[styles.wrapper, style]}>
92
- <NativeAdView nativeAd={nativeAd} style={[styles.container]}>
93
- {/* Ad Badge và Advertiser */}
94
- <View style={styles.adHeader}>
95
- {nativeAd.advertiser && (
96
- <NativeAsset assetType={NativeAssetType.ADVERTISER}>
97
- <Text style={styles.advertiserText} numberOfLines={1}>
98
- {nativeAd.advertiser}
99
- </Text>
94
+ <View style={styles.overlayContainer}>
95
+ <NativeAdView
96
+ key={nativeAd.responseId}
97
+ nativeAd={nativeAd}
98
+ style={[styles.container]}
99
+ >
100
+ {/* Ad Badge và Advertiser */}
101
+ <View style={styles.adHeader}>
102
+ {/* {nativeAd.advertiser && (
103
+ <NativeAsset assetType={NativeAssetType.ADVERTISER}>
104
+ <Text style={styles.advertiserText} numberOfLines={1}>
105
+ {nativeAd.advertiser}
106
+ </Text>
107
+ </NativeAsset>
108
+ )} */}
109
+ </View>
110
+
111
+ {nativeAd.callToAction && (
112
+ <NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
113
+ <View style={[styles.ctaButton, buttonActionStyles]}>
114
+ <Text style={styles.ctaText}>{nativeAd.callToAction}</Text>
115
+ </View>
100
116
  </NativeAsset>
101
117
  )}
102
- </View>
103
118
 
104
- {nativeAd.callToAction && (
105
- <NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
106
- <View style={[styles.ctaButton, buttonActionStyles]}>
107
- <Text style={styles.ctaText}>{nativeAd.callToAction}</Text>
108
- </View>
109
- </NativeAsset>
110
- )}
111
-
112
- {/* Media View - Hiển thị ảnh hoặc video */}
113
- {((nativeAd.images?.length && nativeAd.images.length > 0) ||
114
- nativeAd.mediaContent) && (
115
- <NativeMediaView
116
- style={[
117
- styles.mediaView,
118
- nativeAd.mediaContent?.aspectRatio
119
- ? { aspectRatio: nativeAd.mediaContent.aspectRatio }
120
- : { aspectRatio: 16 / 9 },
121
- ]}
122
- />
123
- )}
119
+ {/* Media View - Hiển thị ảnh hoặc video */}
120
+ {((nativeAd.images?.length && nativeAd.images.length > 0) ||
121
+ nativeAd.mediaContent) && (
122
+ <NativeMediaView
123
+ style={[
124
+ styles.mediaView,
125
+ nativeAd.mediaContent?.aspectRatio
126
+ ? { aspectRatio: nativeAd.mediaContent.aspectRatio }
127
+ : { aspectRatio: 16 / 9 },
128
+ ]}
129
+ />
130
+ )}
124
131
 
125
- {/* Ad Content */}
126
- <View style={styles.contentContainer}>
127
- {nativeAd.icon && (
128
- <NativeAsset assetType={NativeAssetType.ICON}>
129
- <View style={styles.iconContainer}>
130
- <Image
131
- source={{ uri: nativeAd.icon?.url }}
132
- style={styles.icon}
133
- />
132
+ {/* Ad Content */}
133
+ <View style={styles.contentContainer}>
134
+ {nativeAd.icon && (
135
+ <NativeAsset assetType={NativeAssetType.ICON}>
136
+ <View style={styles.iconContainer}>
137
+ <Image
138
+ source={{ uri: nativeAd.icon?.url }}
139
+ style={styles.icon}
140
+ />
141
+ </View>
142
+ </NativeAsset>
143
+ )}
144
+ <View style={styles.contentBody}>
145
+ <View style={styles.vHeader}>
146
+ <Text style={[styles.adBadge, adBadgeStyles]}>Ad</Text>
147
+ {nativeAd.headline && (
148
+ <NativeAsset assetType={NativeAssetType.HEADLINE}>
149
+ <Text
150
+ style={[styles.headline, headlineStyles]}
151
+ numberOfLines={1}
152
+ >
153
+ {nativeAd.headline}
154
+ </Text>
155
+ </NativeAsset>
156
+ )}
134
157
  </View>
135
- </NativeAsset>
136
- )}
137
- <View style={{ flex: 1 }}>
138
- <View style={styles.vHeader}>
139
- <Text style={[styles.adBadge, adBadgeStyles]}>Ad</Text>
140
- {nativeAd.headline && (
141
- <NativeAsset assetType={NativeAssetType.HEADLINE}>
142
- <Text
143
- style={[styles.headline, headlineStyles]}
144
- numberOfLines={1}
145
- >
146
- {nativeAd.headline}
158
+
159
+ {nativeAd.body && (
160
+ <NativeAsset assetType={NativeAssetType.BODY}>
161
+ <Text numberOfLines={2} style={[styles.bodyText, bodyStyles]}>
162
+ {nativeAd.body}
147
163
  </Text>
148
164
  </NativeAsset>
149
165
  )}
150
166
  </View>
151
-
152
- {nativeAd.body && (
153
- <NativeAsset assetType={NativeAssetType.BODY}>
154
- <Text numberOfLines={2} style={[styles.bodyText, bodyStyles]}>
155
- {nativeAd.body}
156
- </Text>
157
- </NativeAsset>
158
- )}
159
167
  </View>
160
- </View>
161
- </NativeAdView>
168
+ </NativeAdView>
169
+ {isRefreshing && (
170
+ <View style={styles.refreshOverlay}>
171
+ <ActivityIndicator size="small" color="#FFFFFF" />
172
+ <Text style={styles.refreshText}>Đang tải quảng cáo mới...</Text>
173
+ </View>
174
+ )}
175
+ </View>
162
176
  </View>
163
177
  );
164
178
  };
@@ -5,6 +5,10 @@ export const styles = StyleSheet.create({
5
5
  width: '100%',
6
6
  alignItems: 'center',
7
7
  },
8
+ overlayContainer: {
9
+ width: '100%',
10
+ position: 'relative',
11
+ },
8
12
  container: {
9
13
  width: '100%',
10
14
  backgroundColor: '#FFFFFF',
@@ -82,6 +86,9 @@ export const styles = StyleSheet.create({
82
86
  alignItems: 'center',
83
87
  padding: 12,
84
88
  },
89
+ contentBody: {
90
+ flex: 1,
91
+ },
85
92
  headline: {
86
93
  fontSize: 16,
87
94
  fontWeight: '700',
@@ -146,4 +153,17 @@ export const styles = StyleSheet.create({
146
153
  flexDirection: 'row',
147
154
  alignItems: 'center',
148
155
  },
156
+ refreshOverlay: {
157
+ ...StyleSheet.absoluteFillObject,
158
+ backgroundColor: 'rgba(0,0,0,0.35)',
159
+ justifyContent: 'center',
160
+ alignItems: 'center',
161
+ padding: 16,
162
+ },
163
+ refreshText: {
164
+ marginTop: 8,
165
+ fontSize: 12,
166
+ color: '#FFFFFF',
167
+ fontWeight: '500',
168
+ },
149
169
  });
@@ -6,6 +6,7 @@ import {
6
6
  type AdConfig,
7
7
  } from '../types/ads-types';
8
8
  import { nativeAdsManager } from '../ads-manager/NativeAdsManager';
9
+ // import { AppState, type AppStateStatus } from 'react-native';
9
10
 
10
11
  // Merge AdConfig với UseNativeAdOptions
11
12
  type UseNativeAdConfig = AdConfig & UseNativeAdOptions;
@@ -36,6 +37,7 @@ export const useNativeAd = (config: UseNativeAdConfig) => {
36
37
  const hasTriedHighPriority = useNativeAdStore((state) =>
37
38
  state.hasTriedHighPriority(adKey)
38
39
  );
40
+ const isRefreshing = useNativeAdStore((state) => state.isRefreshing(adKey));
39
41
 
40
42
  // Zustand actions
41
43
  const preloadAd = useNativeAdStore((state) => state.preloadAd);
@@ -91,6 +93,31 @@ export const useNativeAd = (config: UseNativeAdConfig) => {
91
93
  await refreshAd(adKey);
92
94
  }, [adKey, refreshAd]);
93
95
 
96
+ // const appStateRef = useRef(AppState.currentState);
97
+
98
+ // useEffect(() => {
99
+ // if (!ad) return; // Chỉ refresh nếu đã có ad
100
+
101
+ // const subscription = AppState.addEventListener(
102
+ // 'change',
103
+ // async (nextAppState: AppStateStatus) => {
104
+ // // Khi app chuyển từ background/inactive sang active
105
+ // if (
106
+ // appStateRef.current?.match(/inactive|background/) &&
107
+ // nextAppState === 'active'
108
+ // ) {
109
+ // await handleRefreshAd();
110
+ // }
111
+
112
+ // appStateRef.current = nextAppState;
113
+ // }
114
+ // );
115
+
116
+ // return () => {
117
+ // subscription.remove();
118
+ // };
119
+ // }, [ad, handleRefreshAd]);
120
+
94
121
  // Auto load effect
95
122
  useEffect(() => {
96
123
  if (autoLoad && !ad) {
@@ -121,8 +148,9 @@ export const useNativeAd = (config: UseNativeAdConfig) => {
121
148
  useEffect(() => {
122
149
  if (!ad || !onClicked) return;
123
150
 
124
- const handleClick = (data: any) => {
125
- if (data.adId === ad.adId) {
151
+ const handleClick = (data: { adKey?: string; adId?: string }) => {
152
+ if (data.adKey === ad.adKey) {
153
+ handleRefreshAd();
126
154
  onClicked(ad);
127
155
  }
128
156
  };
@@ -132,14 +160,14 @@ export const useNativeAd = (config: UseNativeAdConfig) => {
132
160
  return () => {
133
161
  nativeAdsManager.off('adClicked', handleClick);
134
162
  };
135
- }, [ad, onClicked]);
163
+ }, [ad, onClicked, handleRefreshAd]);
136
164
 
137
165
  // Impression event listener
138
166
  useEffect(() => {
139
167
  if (!ad || !onImpression) return;
140
168
 
141
- const handleImpression = (data: any) => {
142
- if (data.adId === ad.adId) {
169
+ const handleImpression = (data: { adKey?: string; adId?: string }) => {
170
+ if (data.adKey === ad.adKey) {
143
171
  onImpression(ad);
144
172
  }
145
173
  };
@@ -160,6 +188,7 @@ export const useNativeAd = (config: UseNativeAdConfig) => {
160
188
  isLoaded: ad?.status === AdStatus.LOADED,
161
189
  isDisplayed: ad?.status === AdStatus.DISPLAYED,
162
190
  hasError: ad?.status === AdStatus.ERROR,
191
+ isRefreshing,
163
192
  isUsingHighPriority,
164
193
  hasTriedHighPriority,
165
194
 
@@ -9,6 +9,7 @@ interface NativeAdStore {
9
9
  ads: Map<string, AdInstance>;
10
10
  loadingKeys: Set<string>;
11
11
  errors: Map<string, string>;
12
+ refreshingKeys: Set<string>;
12
13
 
13
14
  // Actions - CHỈ NHẬN CONFIG OBJECT
14
15
  preloadAd: (config: AdConfig) => Promise<void>;
@@ -27,11 +28,13 @@ interface NativeAdStore {
27
28
  getErrorAds: () => AdInstance[];
28
29
  isUsingHighPriority: (adKey: string) => boolean;
29
30
  hasTriedHighPriority: (adKey: string) => boolean;
31
+ isRefreshing: (adKey: string) => boolean;
30
32
 
31
33
  // Internal actions
32
34
  _updateAd: (ad: AdInstance) => void;
33
35
  _removeAd: (adKey: string) => void;
34
36
  _setLoading: (adKey: string, loading: boolean) => void;
37
+ _setRefreshing: (adKey: string, refreshing: boolean) => void;
35
38
  _setError: (adKey: string, error: string | null) => void;
36
39
  _clearError: (adKey: string) => void;
37
40
  }
@@ -42,6 +45,7 @@ export const useNativeAdStore = create<NativeAdStore>()(
42
45
  ads: new Map(),
43
46
  loadingKeys: new Set(),
44
47
  errors: new Map(),
48
+ refreshingKeys: new Set(),
45
49
 
46
50
  // Public actions
47
51
  preloadAd: async (config) => {
@@ -94,7 +98,8 @@ export const useNativeAdStore = create<NativeAdStore>()(
94
98
 
95
99
  refreshAd: async (adKey) => {
96
100
  try {
97
- get()._setLoading(adKey, true);
101
+ // get()._setLoading(adKey, true);
102
+ get()._setRefreshing(adKey, true);
98
103
  get()._clearError(adKey);
99
104
 
100
105
  await nativeAdsManager.refreshAd(adKey);
@@ -109,7 +114,8 @@ export const useNativeAdStore = create<NativeAdStore>()(
109
114
  get()._setError(adKey, errorMessage);
110
115
  console.error(`Failed to refresh ad ${adKey}:`, errorMessage);
111
116
  } finally {
112
- get()._setLoading(adKey, false);
117
+ // get()._setLoading(adKey, false);
118
+ get()._setRefreshing(adKey, false);
113
119
  }
114
120
  },
115
121
 
@@ -119,6 +125,7 @@ export const useNativeAdStore = create<NativeAdStore>()(
119
125
  ads: new Map(),
120
126
  loadingKeys: new Set(),
121
127
  errors: new Map(),
128
+ refreshingKeys: new Set(),
122
129
  });
123
130
  },
124
131
 
@@ -161,6 +168,10 @@ export const useNativeAdStore = create<NativeAdStore>()(
161
168
  return ad?.hasTriedHighPriority || false;
162
169
  },
163
170
 
171
+ isRefreshing: (adKey) => {
172
+ return get().refreshingKeys.has(adKey);
173
+ },
174
+
164
175
  // Internal actions - GIỮ NGUYÊN
165
176
  _updateAd: (ad) => {
166
177
  set((state) => ({
@@ -173,15 +184,18 @@ export const useNativeAdStore = create<NativeAdStore>()(
173
184
  const newAds = new Map(state.ads);
174
185
  const newLoadingKeys = new Set(state.loadingKeys);
175
186
  const newErrors = new Map(state.errors);
187
+ const newRefreshing = new Set(state.refreshingKeys);
176
188
 
177
189
  newAds.delete(adKey);
178
190
  newLoadingKeys.delete(adKey);
179
191
  newErrors.delete(adKey);
192
+ newRefreshing.delete(adKey);
180
193
 
181
194
  return {
182
195
  ads: newAds,
183
196
  loadingKeys: newLoadingKeys,
184
197
  errors: newErrors,
198
+ refreshingKeys: newRefreshing,
185
199
  };
186
200
  });
187
201
  },
@@ -198,6 +212,18 @@ export const useNativeAdStore = create<NativeAdStore>()(
198
212
  });
199
213
  },
200
214
 
215
+ _setRefreshing: (adKey, refreshing) => {
216
+ set((state) => {
217
+ const newRefreshing = new Set(state.refreshingKeys);
218
+ if (refreshing) {
219
+ newRefreshing.add(adKey);
220
+ } else {
221
+ newRefreshing.delete(adKey);
222
+ }
223
+ return { refreshingKeys: newRefreshing };
224
+ });
225
+ },
226
+
201
227
  _setError: (adKey, error) => {
202
228
  set((state) => {
203
229
  const newErrors = new Map(state.errors);
@@ -233,23 +259,32 @@ const setupNativeListeners = () => {
233
259
  nativeAdsManager.on('adLoaded', (ad: AdInstance) => {
234
260
  useNativeAdStore.getState()._updateAd(ad);
235
261
  useNativeAdStore.getState()._setLoading(ad.adKey, false);
262
+ useNativeAdStore.getState()._setRefreshing(ad.adKey, false);
236
263
  useNativeAdStore.getState()._clearError(ad.adKey);
237
264
  });
238
265
 
239
266
  nativeAdsManager.on('adDisplayed', (ad: AdInstance) => {
240
267
  useNativeAdStore.getState()._updateAd(ad);
268
+ useNativeAdStore.getState()._setRefreshing(ad.adKey, false);
241
269
  });
242
270
 
243
271
  nativeAdsManager.on('adError', (ad: AdInstance) => {
244
272
  useNativeAdStore.getState()._updateAd(ad);
245
273
  useNativeAdStore.getState()._setLoading(ad.adKey, false);
274
+ useNativeAdStore.getState()._setRefreshing(ad.adKey, false);
246
275
  useNativeAdStore
247
276
  .getState()
248
277
  ._setError(ad.adKey, ad.error || 'Unknown error');
249
278
  });
250
279
 
280
+ nativeAdsManager.on('adRefreshing', (ad: AdInstance) => {
281
+ useNativeAdStore.getState()._updateAd(ad);
282
+ useNativeAdStore.getState()._setRefreshing(ad.adKey, true);
283
+ });
284
+
251
285
  nativeAdsManager.on('adRemoved', (adKey: string) => {
252
286
  useNativeAdStore.getState()._removeAd(adKey);
287
+ useNativeAdStore.getState()._setRefreshing(adKey, false);
253
288
  });
254
289
 
255
290
  nativeAdsManager.on('allAdsCleared', () => {
@@ -1,6 +1,7 @@
1
1
  export enum AdStatus {
2
2
  IDLE = 'idle',
3
3
  LOADING = 'loading',
4
+ REFRESHING = 'refreshing',
4
5
  LOADED = 'loaded',
5
6
  DISPLAYED = 'displayed',
6
7
  ERROR = 'error',
@@ -45,12 +46,14 @@ export interface AdInstance {
45
46
  retryCount: number;
46
47
  isUsingHighPriority?: boolean; // Track xem đang load high priority hay không
47
48
  hasTriedHighPriority?: boolean; // Track xem đã thử high priority chưa
49
+ isRefreshing?: boolean;
48
50
  }
49
51
 
50
52
  export interface AdManagerState {
51
53
  ads: Map<string, AdInstance>;
52
54
  loadingKeys: Set<string>;
53
55
  displayedKeys: Set<string>;
56
+ refreshingKeys: Set<string>;
54
57
  }
55
58
 
56
59
  export interface AdManagerActions {
@@ -164,6 +167,7 @@ export interface GenericAdManagerState {
164
167
  ads: Map<string, AnyAdInstance>;
165
168
  loadingKeys: Set<string>;
166
169
  displayedKeys: Set<string>;
170
+ refreshingKeys: Set<string>;
167
171
  }
168
172
 
169
173
  export interface GenericAdManagerActions {