@vynix-org/react-native-ads-sdk 0.1.4 → 0.1.6
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/lib/module/ads-manager/AppOpenAdsManager.js +3 -0
- package/lib/module/ads-manager/AppOpenAdsManager.js.map +1 -1
- package/lib/module/ads-manager/InterstitialAdsManager.js +26 -3
- package/lib/module/ads-manager/InterstitialAdsManager.js.map +1 -1
- package/lib/module/ads-manager/NativeAdsManager.js +27 -3
- package/lib/module/ads-manager/NativeAdsManager.js.map +1 -1
- package/lib/module/ads-manager/RewardedAdsManager.js +26 -2
- package/lib/module/ads-manager/RewardedAdsManager.js.map +1 -1
- package/lib/module/ads-manager/RewardedInterstitialAdsManager.js +25 -1
- package/lib/module/ads-manager/RewardedInterstitialAdsManager.js.map +1 -1
- package/lib/module/analytics/AnalyticsManager.js +132 -0
- package/lib/module/analytics/AnalyticsManager.js.map +1 -0
- package/lib/module/analytics/EventTracking.js +11 -0
- package/lib/module/analytics/EventTracking.js.map +1 -0
- package/lib/module/analytics/TrackingManager.js +26 -0
- package/lib/module/analytics/TrackingManager.js.map +1 -0
- package/lib/module/components/BannerAdComponent.js +30 -6
- package/lib/module/components/BannerAdComponent.js.map +1 -1
- package/lib/module/components/NativeAdComponent.js +49 -80
- package/lib/module/components/NativeAdComponent.js.map +1 -1
- package/lib/module/components/NativeStyles.js +22 -14
- package/lib/module/components/NativeStyles.js.map +1 -1
- package/lib/typescript/src/ads-manager/AppOpenAdsManager.d.ts.map +1 -1
- package/lib/typescript/src/ads-manager/InterstitialAdsManager.d.ts.map +1 -1
- package/lib/typescript/src/ads-manager/NativeAdsManager.d.ts.map +1 -1
- package/lib/typescript/src/ads-manager/RewardedAdsManager.d.ts.map +1 -1
- package/lib/typescript/src/ads-manager/RewardedInterstitialAdsManager.d.ts.map +1 -1
- package/lib/typescript/src/analytics/AnalyticsManager.d.ts +50 -0
- package/lib/typescript/src/analytics/AnalyticsManager.d.ts.map +1 -0
- package/lib/typescript/src/analytics/EventTracking.d.ts +14 -0
- package/lib/typescript/src/analytics/EventTracking.d.ts.map +1 -0
- package/lib/typescript/src/analytics/TrackingManager.d.ts +12 -0
- package/lib/typescript/src/analytics/TrackingManager.d.ts.map +1 -0
- package/lib/typescript/src/components/BannerAdComponent.d.ts.map +1 -1
- package/lib/typescript/src/components/NativeAdComponent.d.ts.map +1 -1
- package/lib/typescript/src/components/NativeStyles.d.ts +15 -7
- package/lib/typescript/src/components/NativeStyles.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/ads-manager/AppOpenAdsManager.ts +4 -0
- package/src/ads-manager/InterstitialAdsManager.ts +29 -3
- package/src/ads-manager/NativeAdsManager.ts +31 -2
- package/src/ads-manager/RewardedAdsManager.ts +30 -2
- package/src/ads-manager/RewardedInterstitialAdsManager.ts +30 -1
- package/src/analytics/AnalyticsManager.ts +140 -0
- package/src/analytics/EventTracking.ts +14 -0
- package/src/analytics/TrackingManager.ts +33 -0
- package/src/components/BannerAdComponent.tsx +35 -5
- package/src/components/NativeAdComponent.tsx +35 -79
- package/src/components/NativeStyles.ts +20 -12
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import analytics from '@react-native-firebase/analytics';
|
|
2
|
+
import { EventTracking, type EventParams } from './EventTracking';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Event names cho Ads Analytics
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Analytics Manager để quản lý Firebase Analytics events
|
|
10
|
+
*/
|
|
11
|
+
export class AnalyticsManager {
|
|
12
|
+
private static instance: AnalyticsManager;
|
|
13
|
+
private isEnabled = true;
|
|
14
|
+
private isInitialized = false;
|
|
15
|
+
private initPromise: Promise<void> | null = null;
|
|
16
|
+
|
|
17
|
+
private constructor() {}
|
|
18
|
+
|
|
19
|
+
static getInstance(): AnalyticsManager {
|
|
20
|
+
if (!AnalyticsManager.instance) {
|
|
21
|
+
AnalyticsManager.instance = new AnalyticsManager();
|
|
22
|
+
}
|
|
23
|
+
return AnalyticsManager.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Khởi tạo Analytics Manager
|
|
28
|
+
*/
|
|
29
|
+
async initialize(): Promise<void> {
|
|
30
|
+
if (this.isInitialized) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (this.initPromise) {
|
|
35
|
+
return this.initPromise;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.initPromise = this.initializeInternal();
|
|
39
|
+
return this.initPromise;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private async initializeInternal(): Promise<void> {
|
|
43
|
+
try {
|
|
44
|
+
// Kiểm tra xem analytics có sẵn không
|
|
45
|
+
await analytics().setAnalyticsCollectionEnabled(this.isEnabled);
|
|
46
|
+
this.isInitialized = true;
|
|
47
|
+
console.log('Analytics Manager initialized successfully');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Failed to initialize Analytics Manager:', error);
|
|
50
|
+
// Không throw error để không làm gián đoạn app
|
|
51
|
+
this.isInitialized = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Bật/tắt analytics collection
|
|
57
|
+
*/
|
|
58
|
+
async setEnabled(enabled: boolean): Promise<void> {
|
|
59
|
+
this.isEnabled = enabled;
|
|
60
|
+
try {
|
|
61
|
+
await analytics().setAnalyticsCollectionEnabled(enabled);
|
|
62
|
+
console.log(`Analytics collection ${enabled ? 'enabled' : 'disabled'}`);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('Failed to set analytics collection enabled:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Kiểm tra xem analytics có được bật không
|
|
70
|
+
*/
|
|
71
|
+
isAnalyticsEnabled(): boolean {
|
|
72
|
+
return this.isEnabled;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Log một custom event
|
|
77
|
+
*/
|
|
78
|
+
async logEvent(eventName: string, params?: EventParams): Promise<void> {
|
|
79
|
+
if (!this.isEnabled) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await this.ensureInitialized();
|
|
85
|
+
await analytics().logEvent(eventName, params);
|
|
86
|
+
console.log(`Analytics event logged: ${eventName}`, params);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`Failed to log analytics event ${eventName}:`, error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Log ad event với các parameters chuẩn
|
|
94
|
+
*/
|
|
95
|
+
async logAdEvent(event: EventTracking, params?: EventParams): Promise<void> {
|
|
96
|
+
await this.logEvent(event, params);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async logRequestAdEvent(params?: EventParams): Promise<void> {
|
|
100
|
+
await this.logAdEvent(EventTracking.TRACK_AD_REQUEST, params);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async logClickAdEvent(params?: EventParams): Promise<void> {
|
|
104
|
+
await this.logAdEvent(EventTracking.EVENT_CLICK_AD, params);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async logRevenueAdEvent(params?: EventParams): Promise<void> {
|
|
108
|
+
await this.logAdEvent(EventTracking.PAID_AD_IMPRESSION, params);
|
|
109
|
+
await this.logAdEvent(EventTracking.EVENT_PURCHASE, params);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async logAdLoadedEvent(params?: EventParams): Promise<void> {
|
|
113
|
+
await this.logAdEvent(EventTracking.EVENT_AD_LOADED, params);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Reset analytics data
|
|
118
|
+
*/
|
|
119
|
+
async resetAnalyticsData(): Promise<void> {
|
|
120
|
+
try {
|
|
121
|
+
await this.ensureInitialized();
|
|
122
|
+
await analytics().resetAnalyticsData();
|
|
123
|
+
console.log('Analytics data reset');
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Failed to reset analytics data:', error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Đảm bảo analytics đã được khởi tạo
|
|
131
|
+
*/
|
|
132
|
+
private async ensureInitialized(): Promise<void> {
|
|
133
|
+
if (!this.isInitialized) {
|
|
134
|
+
await this.initialize();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Singleton instance
|
|
140
|
+
export const analyticsManager = AnalyticsManager.getInstance();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export enum EventTracking {
|
|
2
|
+
TRACK_AD_REQUEST = 'track_ad_request',
|
|
3
|
+
PAID_AD_IMPRESSION = 'paid_ad_impression_value',
|
|
4
|
+
EVENT_CLICK_AD = 'event_user_click_ads',
|
|
5
|
+
EVENT_AD_LOADED = 'track_ad_matched_request',
|
|
6
|
+
EVENT_PURCHASE = 'purchase',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface EventParams {
|
|
10
|
+
adUnitId?: string;
|
|
11
|
+
adFormat?: string;
|
|
12
|
+
currency?: string;
|
|
13
|
+
value?: number;
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { analyticsManager } from './AnalyticsManager';
|
|
2
|
+
import type { EventParams } from './EventTracking';
|
|
3
|
+
|
|
4
|
+
export class TrackingManager {
|
|
5
|
+
private static instance: TrackingManager;
|
|
6
|
+
|
|
7
|
+
private constructor() {}
|
|
8
|
+
|
|
9
|
+
static getInstance(): TrackingManager {
|
|
10
|
+
if (!TrackingManager.instance) {
|
|
11
|
+
TrackingManager.instance = new TrackingManager();
|
|
12
|
+
}
|
|
13
|
+
return TrackingManager.instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async onTrackingRequestAd(params?: EventParams) {
|
|
17
|
+
await analyticsManager.logRequestAdEvent(params);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async onTrackingClickAd(params?: EventParams) {
|
|
21
|
+
await analyticsManager.logClickAdEvent(params);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async onTrackingRevenueAd(params?: EventParams) {
|
|
25
|
+
await analyticsManager.logRevenueAdEvent(params);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async onTrackingAdLoaded(params?: EventParams) {
|
|
29
|
+
await analyticsManager.logAdLoadedEvent(params);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const trackingManager = TrackingManager.getInstance();
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import React, { useEffect, useState, useRef } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BannerAd,
|
|
5
|
+
type PaidEvent,
|
|
6
|
+
type RequestOptions,
|
|
7
|
+
} from 'react-native-google-mobile-ads';
|
|
4
8
|
import {
|
|
5
9
|
Placeholder,
|
|
6
10
|
PlaceholderMedia,
|
|
7
11
|
PlaceholderLine,
|
|
8
12
|
Fade,
|
|
9
13
|
} from 'rn-placeholder';
|
|
14
|
+
import { trackingManager } from '../analytics/TrackingManager';
|
|
10
15
|
|
|
11
16
|
interface BannerAdComponentProps {
|
|
12
17
|
adId: string;
|
|
@@ -37,7 +42,7 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
37
42
|
adId,
|
|
38
43
|
adHighpriorityId,
|
|
39
44
|
style,
|
|
40
|
-
size = '
|
|
45
|
+
size = 'ANCHORED_ADAPTIVE_BANNER',
|
|
41
46
|
onAdLoaded,
|
|
42
47
|
onAdFailedToLoad,
|
|
43
48
|
onAdOpened,
|
|
@@ -54,6 +59,7 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
54
59
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
55
60
|
const [error, setError] = useState<string | null>(null);
|
|
56
61
|
const [shouldRender, setShouldRender] = useState(false);
|
|
62
|
+
const [height, setHeight] = useState<number>(50);
|
|
57
63
|
const bannerRef = useRef<any>(null);
|
|
58
64
|
|
|
59
65
|
const [currentAdId, setCurrentAdId] = useState<string>(
|
|
@@ -71,10 +77,15 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
71
77
|
}, [adHighpriorityId]);
|
|
72
78
|
|
|
73
79
|
const handleAdLoaded = () => {
|
|
80
|
+
setHeight(50);
|
|
74
81
|
setIsLoaded(true);
|
|
75
82
|
setError(null);
|
|
76
83
|
console.log('PreloadedBannerAd loaded:', currentAdId);
|
|
77
84
|
onAdLoaded?.();
|
|
85
|
+
trackingManager.onTrackingRequestAd({
|
|
86
|
+
adUnitId: currentAdId,
|
|
87
|
+
adFormat: 'Banner',
|
|
88
|
+
});
|
|
78
89
|
};
|
|
79
90
|
|
|
80
91
|
const handleAdFailedToLoad = (error: Error) => {
|
|
@@ -99,7 +110,7 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
99
110
|
return; // Không gọi onAdFailedToLoad vì sẽ thử lại
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
|
|
113
|
+
setHeight(0);
|
|
103
114
|
setError(error.message);
|
|
104
115
|
onAdFailedToLoad?.(error);
|
|
105
116
|
};
|
|
@@ -117,11 +128,29 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
117
128
|
const handleAdClicked = () => {
|
|
118
129
|
console.log('PreloadedBannerAd clicked');
|
|
119
130
|
onAdClicked?.();
|
|
131
|
+
trackingManager.onTrackingClickAd({
|
|
132
|
+
adUnitId: currentAdId,
|
|
133
|
+
adFormat: 'Banner',
|
|
134
|
+
});
|
|
120
135
|
};
|
|
121
136
|
|
|
122
137
|
const handleAdImpression = () => {
|
|
123
138
|
console.log('PreloadedBannerAd impression');
|
|
124
139
|
onAdImpression?.();
|
|
140
|
+
trackingManager.onTrackingAdLoaded({
|
|
141
|
+
adUnitId: currentAdId,
|
|
142
|
+
adFormat: 'Banner',
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const handlePaid = (paid: PaidEvent) => {
|
|
147
|
+
console.log('PreloadedBannerAd paid:', paid);
|
|
148
|
+
trackingManager.onTrackingRevenueAd({
|
|
149
|
+
adUnitId: currentAdId,
|
|
150
|
+
adFormat: 'Banner',
|
|
151
|
+
currency: paid.currency,
|
|
152
|
+
value: paid.value,
|
|
153
|
+
});
|
|
125
154
|
};
|
|
126
155
|
|
|
127
156
|
if (!shouldRender) {
|
|
@@ -148,7 +177,7 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
148
177
|
|
|
149
178
|
{error && showError && <View />}
|
|
150
179
|
|
|
151
|
-
<View style={{ minHeight:
|
|
180
|
+
<View style={{ minHeight: height }}>
|
|
152
181
|
<BannerAd
|
|
153
182
|
key={currentAdId}
|
|
154
183
|
ref={bannerRef}
|
|
@@ -161,6 +190,7 @@ export const BannerAdComponent: React.FC<BannerAdComponentProps> = ({
|
|
|
161
190
|
onAdClicked={handleAdClicked}
|
|
162
191
|
onAdImpression={handleAdImpression}
|
|
163
192
|
requestOptions={requestOptions}
|
|
193
|
+
onPaid={(paid) => handlePaid(paid)}
|
|
164
194
|
/>
|
|
165
195
|
</View>
|
|
166
196
|
</View>
|
|
@@ -181,7 +211,7 @@ const styles = StyleSheet.create({
|
|
|
181
211
|
justifyContent: 'center',
|
|
182
212
|
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
183
213
|
paddingHorizontal: 20,
|
|
184
|
-
paddingVertical:
|
|
214
|
+
paddingVertical: 12,
|
|
185
215
|
borderRadius: 8,
|
|
186
216
|
},
|
|
187
217
|
loadingText: {
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
5
5
|
ActivityIndicator,
|
|
6
|
-
TouchableOpacity,
|
|
7
6
|
type ViewStyle,
|
|
8
7
|
type TextStyle,
|
|
9
8
|
} from 'react-native';
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
import { useNativeAd } from '../hook/useNativeAd';
|
|
18
17
|
import type { AdInstance } from '../types/ads-types';
|
|
19
18
|
import { styles } from './NativeStyles';
|
|
19
|
+
import { Image } from 'react-native';
|
|
20
20
|
|
|
21
21
|
interface NativeAdComponentProps {
|
|
22
22
|
adKey: string;
|
|
@@ -51,11 +51,8 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
|
|
|
51
51
|
adBadgeStyles,
|
|
52
52
|
headlineStyles,
|
|
53
53
|
bodyStyles,
|
|
54
|
-
ratingStyles,
|
|
55
|
-
priceStyles,
|
|
56
|
-
storeStyles,
|
|
57
54
|
}) => {
|
|
58
|
-
const { ad, isLoading, error, isLoaded
|
|
55
|
+
const { ad, isLoading, error, isLoaded } = useNativeAd({
|
|
59
56
|
adKey,
|
|
60
57
|
adId,
|
|
61
58
|
autoLoad,
|
|
@@ -80,27 +77,12 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
|
|
|
80
77
|
|
|
81
78
|
// Render error state
|
|
82
79
|
if (error) {
|
|
83
|
-
return
|
|
84
|
-
<View style={[styles.wrapper, style]}>
|
|
85
|
-
<View style={[styles.container, styles.centerContent]}>
|
|
86
|
-
<Text style={styles.errorText}>Lỗi: {error}</Text>
|
|
87
|
-
<TouchableOpacity onPress={() => loadAd()} style={styles.retryButton}>
|
|
88
|
-
<Text style={styles.retryText}>Thử lại</Text>
|
|
89
|
-
</TouchableOpacity>
|
|
90
|
-
</View>
|
|
91
|
-
</View>
|
|
92
|
-
);
|
|
80
|
+
return null;
|
|
93
81
|
}
|
|
94
82
|
|
|
95
83
|
// Render empty state
|
|
96
84
|
if (!isLoaded || !ad?.data?.nativeAd) {
|
|
97
|
-
return
|
|
98
|
-
<View style={[styles.wrapper, style]}>
|
|
99
|
-
<View style={[styles.container, styles.centerContent]}>
|
|
100
|
-
<Text style={styles.emptyText}>Không có quảng cáo</Text>
|
|
101
|
-
</View>
|
|
102
|
-
</View>
|
|
103
|
-
);
|
|
85
|
+
return null;
|
|
104
86
|
}
|
|
105
87
|
|
|
106
88
|
const nativeAd = ad.data.nativeAd as NativeAd;
|
|
@@ -110,9 +92,6 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
|
|
|
110
92
|
<NativeAdView nativeAd={nativeAd} style={[styles.container]}>
|
|
111
93
|
{/* Ad Badge và Advertiser */}
|
|
112
94
|
<View style={styles.adHeader}>
|
|
113
|
-
{/* <NativeAsset assetType={NativeAssetType.ADVERTISER}> */}
|
|
114
|
-
<Text style={[styles.adBadge, adBadgeStyles]}>Ad</Text>
|
|
115
|
-
{/* </NativeAsset> */}
|
|
116
95
|
{nativeAd.advertiser && (
|
|
117
96
|
<NativeAsset assetType={NativeAssetType.ADVERTISER}>
|
|
118
97
|
<Text style={styles.advertiserText} numberOfLines={1}>
|
|
@@ -122,6 +101,14 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
|
|
|
122
101
|
)}
|
|
123
102
|
</View>
|
|
124
103
|
|
|
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
|
+
|
|
125
112
|
{/* Media View - Hiển thị ảnh hoặc video */}
|
|
126
113
|
{((nativeAd.images?.length && nativeAd.images.length > 0) ||
|
|
127
114
|
nativeAd.mediaContent) && (
|
|
@@ -137,71 +124,40 @@ export const NativeAdComponent: React.FC<NativeAdComponentProps> = ({
|
|
|
137
124
|
|
|
138
125
|
{/* Ad Content */}
|
|
139
126
|
<View style={styles.contentContainer}>
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
{/* Body */}
|
|
150
|
-
{nativeAd.body && (
|
|
151
|
-
<NativeAsset assetType={NativeAssetType.BODY}>
|
|
152
|
-
<Text style={[styles.bodyText, bodyStyles]} numberOfLines={3}>
|
|
153
|
-
{nativeAd.body}
|
|
154
|
-
</Text>
|
|
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
|
+
/>
|
|
134
|
+
</View>
|
|
155
135
|
</NativeAsset>
|
|
156
136
|
)}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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}
|
|
166
147
|
</Text>
|
|
167
148
|
</NativeAsset>
|
|
168
149
|
)}
|
|
150
|
+
</View>
|
|
169
151
|
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{nativeAd.price}
|
|
175
|
-
</Text>
|
|
176
|
-
</NativeAsset>
|
|
177
|
-
)}
|
|
178
|
-
|
|
179
|
-
{/* Store */}
|
|
180
|
-
{nativeAd.store && (
|
|
181
|
-
<NativeAsset assetType={NativeAssetType.STORE}>
|
|
182
|
-
<Text style={[styles.storeText, storeStyles]} numberOfLines={1}>
|
|
183
|
-
{nativeAd.store}
|
|
152
|
+
{nativeAd.body && (
|
|
153
|
+
<NativeAsset assetType={NativeAssetType.BODY}>
|
|
154
|
+
<Text numberOfLines={2} style={[styles.bodyText, bodyStyles]}>
|
|
155
|
+
{nativeAd.body}
|
|
184
156
|
</Text>
|
|
185
157
|
</NativeAsset>
|
|
186
158
|
)}
|
|
187
159
|
</View>
|
|
188
|
-
|
|
189
|
-
{/* Call To Action Button */}
|
|
190
|
-
{nativeAd.callToAction && (
|
|
191
|
-
<NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
|
|
192
|
-
<View style={[styles.ctaButton, buttonActionStyles]}>
|
|
193
|
-
<Text style={styles.ctaText}>{nativeAd.callToAction}</Text>
|
|
194
|
-
</View>
|
|
195
|
-
</NativeAsset>
|
|
196
|
-
)}
|
|
197
160
|
</View>
|
|
198
|
-
|
|
199
|
-
{/* Icon */}
|
|
200
|
-
{/* {nativeAd.icon && (
|
|
201
|
-
<NativeAsset assetType={NativeAssetType.ICON}>
|
|
202
|
-
<View style={styles.iconContainer} />
|
|
203
|
-
</NativeAsset>
|
|
204
|
-
)} */}
|
|
205
161
|
</NativeAdView>
|
|
206
162
|
</View>
|
|
207
163
|
);
|
|
@@ -55,13 +55,14 @@ export const styles = StyleSheet.create({
|
|
|
55
55
|
backgroundColor: '#F9F9F9',
|
|
56
56
|
},
|
|
57
57
|
adBadge: {
|
|
58
|
-
backgroundColor: '#
|
|
58
|
+
backgroundColor: '#57BF87',
|
|
59
59
|
paddingHorizontal: 6,
|
|
60
60
|
paddingVertical: 2,
|
|
61
61
|
borderRadius: 3,
|
|
62
62
|
fontSize: 10,
|
|
63
|
+
marginEnd: 10,
|
|
63
64
|
fontWeight: '700',
|
|
64
|
-
color: '#
|
|
65
|
+
color: '#fff',
|
|
65
66
|
},
|
|
66
67
|
advertiserText: {
|
|
67
68
|
fontSize: 11,
|
|
@@ -75,23 +76,22 @@ export const styles = StyleSheet.create({
|
|
|
75
76
|
minHeight: 180,
|
|
76
77
|
maxHeight: 250,
|
|
77
78
|
alignSelf: 'center',
|
|
78
|
-
backgroundColor: '#F0F0F0',
|
|
79
79
|
},
|
|
80
80
|
contentContainer: {
|
|
81
|
+
flexDirection: 'row',
|
|
82
|
+
alignItems: 'center',
|
|
81
83
|
padding: 12,
|
|
82
84
|
},
|
|
83
85
|
headline: {
|
|
84
86
|
fontSize: 16,
|
|
85
87
|
fontWeight: '700',
|
|
86
88
|
color: '#000',
|
|
87
|
-
marginBottom: 6,
|
|
88
89
|
lineHeight: 22,
|
|
89
90
|
},
|
|
90
91
|
bodyText: {
|
|
91
92
|
fontSize: 13,
|
|
92
93
|
color: '#666',
|
|
93
94
|
lineHeight: 18,
|
|
94
|
-
marginBottom: 10,
|
|
95
95
|
},
|
|
96
96
|
infoRow: {
|
|
97
97
|
flexDirection: 'row',
|
|
@@ -118,7 +118,8 @@ export const styles = StyleSheet.create({
|
|
|
118
118
|
ctaButton: {
|
|
119
119
|
backgroundColor: '#007AFF',
|
|
120
120
|
paddingVertical: 10,
|
|
121
|
-
|
|
121
|
+
marginHorizontal: 20,
|
|
122
|
+
marginBottom: 10,
|
|
122
123
|
borderRadius: 6,
|
|
123
124
|
alignItems: 'center',
|
|
124
125
|
justifyContent: 'center',
|
|
@@ -129,13 +130,20 @@ export const styles = StyleSheet.create({
|
|
|
129
130
|
color: '#FFFFFF',
|
|
130
131
|
},
|
|
131
132
|
iconContainer: {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
height: 36,
|
|
137
|
-
borderRadius: 18,
|
|
133
|
+
width: 40,
|
|
134
|
+
height: 40,
|
|
135
|
+
borderRadius: 8,
|
|
136
|
+
marginEnd: 10,
|
|
138
137
|
overflow: 'hidden',
|
|
139
138
|
backgroundColor: '#F0F0F0',
|
|
140
139
|
},
|
|
140
|
+
icon: {
|
|
141
|
+
width: '100%',
|
|
142
|
+
height: '100%',
|
|
143
|
+
resizeMode: 'cover',
|
|
144
|
+
},
|
|
145
|
+
vHeader: {
|
|
146
|
+
flexDirection: 'row',
|
|
147
|
+
alignItems: 'center',
|
|
148
|
+
},
|
|
141
149
|
});
|