781 lines
No EOL
20 KiB
TypeScript
781 lines
No EOL
20 KiB
TypeScript
|
|
import {
|
|
View,
|
|
Text,
|
|
Image,
|
|
StyleSheet,
|
|
Dimensions,
|
|
Platform,
|
|
StatusBar,
|
|
FlatList,
|
|
TouchableOpacity,
|
|
SafeAreaView,
|
|
Modal,
|
|
ScrollView,
|
|
ActivityIndicator,
|
|
Linking,
|
|
} from "react-native";
|
|
import { useState, useEffect } from "react";
|
|
import { BlurView } from "expo-blur";
|
|
import { useRouter } from "expo-router";
|
|
import { IconSymbol } from "@/components/ui/IconSymbol";
|
|
import { useColorScheme } from "@/hooks/useColorScheme";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
|
|
const { width, height } = Dimensions.get("window");
|
|
|
|
// API endpoint - use your own IP address here
|
|
const API_URL = "https://dev.dashboard.planpostai.com/news/api/news";
|
|
|
|
// TypeScript interfaces
|
|
interface Paragraph {
|
|
content: string;
|
|
}
|
|
|
|
interface NewsItem {
|
|
id: number;
|
|
created_at: string;
|
|
title: string;
|
|
description: string;
|
|
image_url: string;
|
|
image_path?: string;
|
|
paragraphs: string[];
|
|
}
|
|
|
|
interface AdItem {
|
|
id: string;
|
|
isAd: boolean;
|
|
adIndex: number;
|
|
}
|
|
|
|
type FeedItem = NewsItem | AdItem;
|
|
|
|
// Type guard to check if an item is an ad
|
|
const isAd = (item: FeedItem): item is AdItem => {
|
|
return (item as AdItem).isAd === true;
|
|
};
|
|
|
|
// Single ad data
|
|
const singleAd = {
|
|
id: 1,
|
|
title: "Internet Packages",
|
|
description:
|
|
"Get the latest data offers...",
|
|
image:
|
|
"https://cdn01da.grameenphone.com/sites/default/files/2023-09/GP_013_New_Number_Series-1060-x-764.jpg",
|
|
url: "https://www.premium-headphones.com",
|
|
};
|
|
|
|
export default function NewsScreen(): JSX.Element {
|
|
const colorScheme = useColorScheme();
|
|
const router = useRouter();
|
|
const insets = useSafeAreaInsets();
|
|
const [selectedNews, setSelectedNews] = useState<NewsItem | null>(null);
|
|
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
|
const [newsData, setNewsData] = useState<NewsItem[]>([]);
|
|
const [processedData, setProcessedData] = useState<FeedItem[]>([]);
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Fetch news data from API
|
|
useEffect(() => {
|
|
fetchNews();
|
|
}, []);
|
|
|
|
// Process news data to add ads after every 4 news items
|
|
useEffect(() => {
|
|
const processed: FeedItem[] = [];
|
|
|
|
newsData.forEach((item, index) => {
|
|
processed.push(item);
|
|
if ((index + 1) % 4 === 0 && index !== newsData.length - 1) {
|
|
processed.push({
|
|
id: `ad-${Math.floor(index / 4)}`,
|
|
isAd: true,
|
|
adIndex: Math.floor(index / 4),
|
|
});
|
|
}
|
|
});
|
|
|
|
setProcessedData(processed);
|
|
}, [newsData]);
|
|
|
|
const fetchNews = async (): Promise<void> => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Set a timeout to prevent hanging requests
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
|
|
const response = await fetch(API_URL, {
|
|
signal: controller.signal,
|
|
});
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
}
|
|
|
|
const responseJson = await response.json();
|
|
|
|
// Extract data from the nested structure
|
|
const data = responseJson.data || responseJson;
|
|
|
|
if (!data || !Array.isArray(data)) {
|
|
throw new Error("Invalid data format received from API");
|
|
}
|
|
|
|
// Process the data to ensure it has the required format
|
|
const processedData: NewsItem[] = data.map((item: any) => ({
|
|
...item,
|
|
// If paragraphs don't exist, create them from description
|
|
paragraphs: item.paragraphs || [
|
|
item.description.substring(0, item.description.length / 3),
|
|
item.description.substring(
|
|
item.description.length / 3,
|
|
(2 * item.description.length) / 3
|
|
),
|
|
item.description.substring((2 * item.description.length) / 3),
|
|
],
|
|
}));
|
|
|
|
setNewsData(processedData);
|
|
} catch (err) {
|
|
console.error("Failed to fetch news:", err);
|
|
setError(
|
|
err instanceof Error ? err.message : "An unknown error occurred"
|
|
);
|
|
setNewsData([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateString: string): string => {
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString("en-US", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
};
|
|
|
|
// Function to truncate text to 99% and add ellipsis
|
|
const truncateDescription = (text: string): string => {
|
|
const truncateLength = Math.floor(text.length * 0.99);
|
|
return text.substring(0, truncateLength) + "...";
|
|
};
|
|
|
|
const openNewsModal = (news: NewsItem): void => {
|
|
setSelectedNews(news);
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const closeNewsModal = (): void => {
|
|
setModalVisible(false);
|
|
};
|
|
|
|
// Navigate to ad website
|
|
const navigateToAdPage = (url: string): void => {
|
|
// Open the URL in the device's browser
|
|
Linking.openURL(url).catch((err) =>
|
|
console.error("Couldn't open URL: ", err)
|
|
);
|
|
};
|
|
|
|
// Advertisement component with full screen layout
|
|
const AdvertisementCard = ({ index }: { index: number }): JSX.Element => {
|
|
return (
|
|
<View style={styles.fullScreenItem}>
|
|
<View style={styles.adHeader}>
|
|
<Text style={[styles.adLabel, { color: "#fff" }]}>SPONSORED</Text>
|
|
</View>
|
|
|
|
{/* Full screen ad with vertical layout */}
|
|
<TouchableOpacity
|
|
style={styles.fullScreenAdContainer}
|
|
onPress={() => navigateToAdPage(singleAd.url)}
|
|
activeOpacity={0.9}
|
|
>
|
|
{/* Image first */}
|
|
<View style={styles.adImageContainer}>
|
|
<Image
|
|
source={{ uri: singleAd.image }}
|
|
style={styles.singleAdImage}
|
|
resizeMode="cover"
|
|
/>
|
|
</View>
|
|
|
|
{/* Content below image */}
|
|
<View style={styles.singleAdContent}>
|
|
<Text style={styles.singleAdTitle}>{singleAd.title}</Text>
|
|
<Text style={styles.singleAdDescription}>
|
|
{singleAd.description}
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderNewsItem = ({ item }: { item: FeedItem }): JSX.Element => {
|
|
if (isAd(item)) {
|
|
return <AdvertisementCard index={item.adIndex} />;
|
|
}
|
|
|
|
// Truncate description to 99% and add ellipsis
|
|
const truncatedDescription = truncateDescription(item.paragraphs[0]);
|
|
|
|
return (
|
|
<View style={styles.newsItemContainer}>
|
|
{/* News image - 30% of screen height */}
|
|
<View style={styles.imageContainer}>
|
|
<Image
|
|
source={{ uri: item.image_url }}
|
|
style={styles.newsImage}
|
|
resizeMode="cover"
|
|
/>
|
|
</View>
|
|
|
|
{/* Content area - 70% of screen height */}
|
|
<View style={styles.contentSection}>
|
|
{Platform.OS === "ios" ? (
|
|
<BlurView
|
|
intensity={80}
|
|
tint="dark"
|
|
style={[
|
|
styles.blurContainer,
|
|
{ backgroundColor: "rgba(167, 61, 228, 0.75)" },
|
|
]}
|
|
>
|
|
<ScrollView
|
|
style={styles.scrollableContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View style={styles.textContainer}>
|
|
<Text style={styles.date}>{formatDate(item.created_at)}</Text>
|
|
|
|
{/* Title first as requested */}
|
|
<Text style={styles.title}>{item.title}</Text>
|
|
|
|
{/* Show 99% of description with ellipsis */}
|
|
<Text style={styles.paragraph}>{truncatedDescription}</Text>
|
|
|
|
{/* Read more button positioned on the right */}
|
|
<View style={styles.readMoreContainer}>
|
|
<TouchableOpacity
|
|
style={styles.readMoreButton}
|
|
onPress={() => openNewsModal(item)}
|
|
>
|
|
<Text style={styles.readMoreText}>Read more</Text>
|
|
<IconSymbol
|
|
name="chevron.right"
|
|
size={16}
|
|
color="#ffffff"
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</BlurView>
|
|
) : (
|
|
<View style={[styles.blurContainer, styles.androidBlur]}>
|
|
<ScrollView
|
|
style={styles.scrollableContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View style={styles.textContainer}>
|
|
<Text style={styles.date}>{formatDate(item.created_at)}</Text>
|
|
|
|
{/* Title first as requested */}
|
|
<Text style={styles.title}>{item.title}</Text>
|
|
|
|
{/* Show 99% of description with ellipsis */}
|
|
<Text style={styles.paragraph}>{truncatedDescription}</Text>
|
|
|
|
{/* Read more button positioned on the right */}
|
|
<View style={styles.readMoreContainer}>
|
|
<TouchableOpacity
|
|
style={styles.readMoreButton}
|
|
onPress={() => openNewsModal(item)}
|
|
>
|
|
<Text style={styles.readMoreText}>Read more</Text>
|
|
<IconSymbol
|
|
name="chevron.right"
|
|
size={16}
|
|
color="#ffffff"
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderErrorView = (): JSX.Element => (
|
|
<View
|
|
style={[
|
|
styles.errorContainer,
|
|
{ backgroundColor: colorScheme === "dark" ? "#1e1e1e" : "#ffffff" },
|
|
]}
|
|
>
|
|
<IconSymbol name="exclamationmark.triangle" size={50} color="#FF3B30" />
|
|
<Text
|
|
style={[
|
|
styles.errorTitle,
|
|
{ color: colorScheme === "dark" ? "#fff" : "#000" },
|
|
]}
|
|
>
|
|
Connection Error
|
|
</Text>
|
|
<Text
|
|
style={[
|
|
styles.errorMessage,
|
|
{ color: colorScheme === "dark" ? "#ddd" : "#666" },
|
|
]}
|
|
>
|
|
{error}
|
|
</Text>
|
|
<TouchableOpacity style={styles.retryButton} onPress={fetchNews}>
|
|
<Text style={styles.retryButtonText}>Retry</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.container,
|
|
{ backgroundColor: colorScheme === "dark" ? "#121212" : "#f0f2f5" },
|
|
]}
|
|
>
|
|
<StatusBar
|
|
translucent
|
|
backgroundColor="transparent"
|
|
barStyle={colorScheme === "dark" ? "light-content" : "dark-content"}
|
|
/>
|
|
|
|
{/* Custom Navbar */}
|
|
<SafeAreaView
|
|
style={{
|
|
backgroundColor: colorScheme === "dark" ? "#121212" : "#ffffff",
|
|
}}
|
|
>
|
|
<View
|
|
style={[
|
|
styles.navbar,
|
|
{
|
|
backgroundColor: colorScheme === "dark" ? "#121212" : "#ffffff",
|
|
paddingTop: Platform.OS === "android" ? insets.top + 8 : 8,
|
|
paddingBottom: 12,
|
|
borderBottomColor:
|
|
colorScheme === "dark" ? "#2c2c2c" : "rgba(150, 150, 150, 0.2)",
|
|
borderBottomWidth: 1,
|
|
},
|
|
]}
|
|
>
|
|
<TouchableOpacity
|
|
style={styles.backButton}
|
|
onPress={() => router.back()}
|
|
>
|
|
<IconSymbol
|
|
name="chevron.left"
|
|
size={24}
|
|
color={colorScheme === "dark" ? "#ffffff" : "#000000"}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.brandContainer}>
|
|
<Text
|
|
style={[
|
|
styles.brandText,
|
|
{ color: colorScheme === "dark" ? "#ffffff" : "#000000" },
|
|
]}
|
|
>
|
|
30 Second
|
|
</Text>
|
|
<Text style={styles.brandAccent}>News</Text>
|
|
</View>
|
|
|
|
<TouchableOpacity style={styles.refreshButton} onPress={fetchNews}>
|
|
<IconSymbol
|
|
name="arrow.clockwise"
|
|
size={20}
|
|
color={colorScheme === "dark" ? "#ffffff" : "#000000"}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</SafeAreaView>
|
|
|
|
{/* Loading Indicator */}
|
|
{loading ? (
|
|
<View
|
|
style={[
|
|
styles.loadingContainer,
|
|
{ backgroundColor: colorScheme === "dark" ? "#121212" : "#f0f2f5" },
|
|
]}
|
|
>
|
|
<ActivityIndicator size="large" color="#007AFF" />
|
|
<Text
|
|
style={[
|
|
styles.loadingText,
|
|
{ color: colorScheme === "dark" ? "#fff" : "#007AFF" },
|
|
]}
|
|
>
|
|
Loading news...
|
|
</Text>
|
|
</View>
|
|
) : error ? (
|
|
renderErrorView()
|
|
) : newsData.length === 0 ? (
|
|
<View
|
|
style={[
|
|
styles.errorContainer,
|
|
{ backgroundColor: colorScheme === "dark" ? "#1e1e1e" : "#ffffff" },
|
|
]}
|
|
>
|
|
<IconSymbol name="newspaper" size={50} color="#007AFF" />
|
|
<Text
|
|
style={[
|
|
styles.errorTitle,
|
|
{ color: colorScheme === "dark" ? "#fff" : "#000" },
|
|
]}
|
|
>
|
|
No News Found
|
|
</Text>
|
|
<Text
|
|
style={[
|
|
styles.errorMessage,
|
|
{ color: colorScheme === "dark" ? "#ddd" : "#666" },
|
|
]}
|
|
>
|
|
We couldn't find any news articles. Please try again later.
|
|
</Text>
|
|
<TouchableOpacity style={styles.retryButton} onPress={fetchNews}>
|
|
<Text style={styles.retryButtonText}>Refresh</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
) : (
|
|
/* Show all news items with ads after every 4 items */
|
|
<FlatList
|
|
data={processedData}
|
|
renderItem={renderNewsItem}
|
|
keyExtractor={(item) => item.id.toString()}
|
|
pagingEnabled
|
|
showsVerticalScrollIndicator={false}
|
|
snapToAlignment="start"
|
|
decelerationRate="fast"
|
|
snapToInterval={height}
|
|
/>
|
|
)}
|
|
|
|
{/* News Detail Modal with text X close button */}
|
|
<Modal
|
|
animationType="slide"
|
|
transparent={true}
|
|
visible={modalVisible}
|
|
onRequestClose={closeNewsModal}
|
|
>
|
|
<View style={styles.modalContainer}>
|
|
<View
|
|
style={[
|
|
styles.modalContent,
|
|
{ backgroundColor: "rgba(167, 61, 228, 0.95)" },
|
|
]}
|
|
>
|
|
{/* Text X close button instead of icon */}
|
|
<TouchableOpacity
|
|
style={styles.closeIconButton}
|
|
onPress={closeNewsModal}
|
|
>
|
|
<Text style={styles.closeButtonText}>X</Text>
|
|
</TouchableOpacity>
|
|
|
|
{/* Modal Content - Title and Description */}
|
|
{selectedNews && (
|
|
<ScrollView
|
|
style={styles.modalScrollView}
|
|
showsVerticalScrollIndicator={true}
|
|
contentContainerStyle={styles.modalScrollViewContent}
|
|
>
|
|
{/* Title */}
|
|
<Text style={styles.modalNewsTitle}>{selectedNews.title}</Text>
|
|
|
|
{/* Date */}
|
|
<Text style={styles.modalDate}>
|
|
{formatDate(selectedNews.created_at)}
|
|
</Text>
|
|
|
|
{/* All paragraphs */}
|
|
{selectedNews.paragraphs.map((paragraph, index) => (
|
|
<Text key={index} style={styles.modalParagraph}>
|
|
{paragraph}
|
|
</Text>
|
|
))}
|
|
|
|
{/* Extra padding at the bottom for better scrolling */}
|
|
<View style={styles.modalBottomPadding} />
|
|
</ScrollView>
|
|
)}
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
navbar: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
paddingHorizontal: 16,
|
|
},
|
|
backButton: {
|
|
padding: 8,
|
|
borderRadius: 20,
|
|
},
|
|
brandContainer: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
brandText: {
|
|
fontSize: 20,
|
|
fontWeight: "700",
|
|
},
|
|
brandAccent: {
|
|
fontSize: 20,
|
|
fontWeight: "700",
|
|
color: "#007AFF",
|
|
marginLeft: 4,
|
|
},
|
|
refreshButton: {
|
|
padding: 8,
|
|
borderRadius: 20,
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
loadingText: {
|
|
marginTop: 10,
|
|
fontSize: 16,
|
|
color: "#007AFF",
|
|
},
|
|
errorContainer: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
padding: 20,
|
|
},
|
|
errorTitle: {
|
|
fontSize: 20,
|
|
fontWeight: "bold",
|
|
marginTop: 16,
|
|
marginBottom: 8,
|
|
},
|
|
errorMessage: {
|
|
fontSize: 16,
|
|
textAlign: "center",
|
|
marginBottom: 20,
|
|
color: "#666",
|
|
},
|
|
retryButton: {
|
|
backgroundColor: "#007AFF",
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 10,
|
|
borderRadius: 8,
|
|
},
|
|
retryButtonText: {
|
|
color: "#fff",
|
|
fontWeight: "600",
|
|
fontSize: 16,
|
|
},
|
|
newsItemContainer: {
|
|
height: height,
|
|
width: width,
|
|
position: "relative",
|
|
},
|
|
imageContainer: {
|
|
height: height * 0.3, // 30% of screen height for image
|
|
width: width,
|
|
overflow: "hidden",
|
|
},
|
|
newsImage: {
|
|
width: "100%",
|
|
height: "100%",
|
|
},
|
|
contentSection: {
|
|
height: height * 0.7, // 70% of screen height for content
|
|
width: width,
|
|
},
|
|
blurContainer: {
|
|
flex: 1,
|
|
borderTopLeftRadius: 0,
|
|
borderTopRightRadius: 0,
|
|
overflow: "hidden",
|
|
},
|
|
androidBlur: {
|
|
backgroundColor: "rgba(167, 61, 228, 0.75)", // Purple background from the provided code
|
|
},
|
|
scrollableContent: {
|
|
flex: 1,
|
|
},
|
|
textContainer: {
|
|
padding: 20,
|
|
paddingTop: 20,
|
|
paddingBottom: 30,
|
|
},
|
|
date: {
|
|
color: "#ccc",
|
|
fontSize: 12,
|
|
marginBottom: 10,
|
|
},
|
|
title: {
|
|
color: "#fff",
|
|
fontSize: 18,
|
|
fontWeight: "bold",
|
|
marginBottom: 15,
|
|
},
|
|
paragraph: {
|
|
color: "#fff",
|
|
fontSize: 15,
|
|
lineHeight: 22,
|
|
marginBottom: 15,
|
|
},
|
|
// Read more button styles
|
|
readMoreContainer: {
|
|
flexDirection: "row",
|
|
justifyContent: "flex-end",
|
|
marginTop: 20,
|
|
},
|
|
readMoreButton: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
paddingVertical: 10,
|
|
paddingHorizontal: 16,
|
|
borderRadius: 8,
|
|
},
|
|
readMoreText: {
|
|
color: "#ffffff",
|
|
fontWeight: "600",
|
|
fontSize: 16,
|
|
marginRight: 5,
|
|
},
|
|
modalContainer: {
|
|
flex: 1,
|
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
},
|
|
modalContent: {
|
|
flex: 1,
|
|
marginTop: 60, // Space from top
|
|
marginBottom: 20, // Space from bottom
|
|
marginHorizontal: 15, // Space from sides
|
|
backgroundColor: "rgba(167, 61, 228, 0.95)", // Purple background
|
|
borderRadius: 20,
|
|
overflow: "hidden",
|
|
},
|
|
modalScrollView: {
|
|
flex: 1,
|
|
},
|
|
modalScrollViewContent: {
|
|
padding: 20,
|
|
paddingTop: 25,
|
|
},
|
|
modalNewsTitle: {
|
|
fontSize: 24,
|
|
fontWeight: "bold",
|
|
marginBottom: 10,
|
|
color: "#ffffff",
|
|
marginTop: 15, // Add space at the top for the close button
|
|
},
|
|
modalDate: {
|
|
fontSize: 14,
|
|
color: "rgba(255, 255, 255, 0.7)",
|
|
marginBottom: 20,
|
|
},
|
|
modalParagraph: {
|
|
fontSize: 17,
|
|
lineHeight: 24,
|
|
marginBottom: 16,
|
|
color: "#ffffff",
|
|
},
|
|
closeIconButton: {
|
|
position: "absolute",
|
|
top: 15,
|
|
right: 15,
|
|
zIndex: 10,
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 18, // Half of width/height
|
|
backgroundColor: "rgba(255, 255, 255, 0.3)",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
closeButtonText: {
|
|
color: "#ffffff",
|
|
fontSize: 20,
|
|
fontWeight: "bold",
|
|
},
|
|
modalBottomPadding: {
|
|
height: 40, // Extra padding at the bottom for better scrolling
|
|
},
|
|
fullScreenItem: {
|
|
height: height,
|
|
width: width,
|
|
backgroundColor: "#ffffff",
|
|
},
|
|
adHeader: {
|
|
backgroundColor: "#007AFF",
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 12,
|
|
alignItems: "center",
|
|
},
|
|
adLabel: {
|
|
fontSize: 12,
|
|
fontWeight: "bold",
|
|
color: "#fff",
|
|
},
|
|
// Full screen ad styles
|
|
fullScreenAdContainer: {
|
|
height: height,
|
|
width: width,
|
|
},
|
|
adImageContainer: {
|
|
height: height * 0.5, // 70% for image
|
|
width: width,
|
|
},
|
|
singleAdImage: {
|
|
width: "100%",
|
|
height: "100%",
|
|
},
|
|
singleAdContent: {
|
|
height: height * 0.5, // 30% for content
|
|
width: width,
|
|
padding: 20,
|
|
backgroundColor: "#ffffff",
|
|
},
|
|
singleAdTitle: {
|
|
fontSize: 24,
|
|
fontWeight: "bold",
|
|
marginBottom: 15,
|
|
color: "#000",
|
|
},
|
|
singleAdDescription: {
|
|
fontSize: 16,
|
|
lineHeight: 24,
|
|
color: "#333",
|
|
},
|
|
}); |