import { useState, useMemo } from 'react';
import { View, Text, ScrollView, Image, ActivityIndicator, StyleSheet, SafeAreaView, TouchableOpacity, FlatList } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { useQuery } from '@tanstack/react-query';
import { useArticle, useOutlet } from '@/hooks/useApi';
import { useLanguage } from '@/contexts/LanguageContext';
import ArticleCard from '@/components/ArticleCard';
import LanguageModal from '@/components/LanguageModal';
import CommentsDrawer from '@/components/CommentsDrawer';
import { API_BASE_URL } from '@/lib/config';
export default function ArticleScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const { language, setLanguage } = useLanguage();
const [imageLoaded, setImageLoaded] = useState(false);
const [showLanguage, setShowLanguage] = useState(false);
const [showComments, setShowComments] = useState(false);
const { data: article, isLoading, error } = useArticle(id || '', language, true);
// Get outlet data from article
const articleData = article as any;
const outletId = articleData?.outletId || articleData?.outlet_id;
const { data: outlet } = useOutlet(outletId || '', language);
// Fetch latest articles for the feed
// TODO: Re-enable when news-api has a compatible feed endpoint
const { data: latestArticles } = useQuery({
queryKey: ['/api/feed', { limit: 20, exclude: id }],
queryFn: async () => {
const response = await fetch(`${API_BASE_URL}/api/v1/${language}/articles/latest?limit=20`);
return response.json();
},
enabled: false, // Disabled until API structure matches frontend expectations
});
// Clean and process body content
const cleanedBody = useMemo(() => {
if (!article || !(article as any).body) return '';
const body = (article as any).body;
// Remove "originally published" line
const pattern = /^\s*(?:This article was\s+)?originally published\s+(?:at|on)\s+\S+\.?\s*(?:\r?\n){1,2}/i;
const cleaned = body.replace(pattern, '').trimStart();
// Truncate at 3000 characters but complete the sentence
if (cleaned.length <= 3000) return cleaned;
const truncatePoint = 3000;
const afterTruncate = cleaned.substring(truncatePoint);
const sentenceEndMatch = afterTruncate.match(/[.!?](?:\s|$)/);
if (sentenceEndMatch && sentenceEndMatch.index !== undefined) {
const endIndex = truncatePoint + sentenceEndMatch.index + 1;
return cleaned.substring(0, endIndex);
}
return cleaned.substring(0, 3000);
}, [article]);
const getInitials = (name: string) => {
return name
.split(' ')
.map(word => word[0])
.join('')
.toUpperCase()
.slice(0, 2);
};
// Parse body content for rendering
const parseBodyContent = (text: string) => {
const lines = text.split('\n');
const elements: Array<{ type: 'subtitle' | 'paragraph', content: string, key: string }> = [];
let currentParagraph: string[] = [];
let keyIndex = 0;
const flushParagraph = () => {
if (currentParagraph.length > 0) {
const paragraphText = currentParagraph.join('\n').trim();
if (paragraphText) {
elements.push({
type: 'paragraph',
content: paragraphText,
key: `p-${keyIndex++}`
});
}
currentParagraph = [];
}
};
lines.forEach((line) => {
const trimmedLine = line.trim();
// Check for subtitle patterns
const fullLineSubtitle = trimmedLine.match(/^\*\*(.*?)\*\*$/);
const prefixedSubtitle = trimmedLine.match(/^\s*\*\*(.+?)\*\*\s*(.*)$/);
if (fullLineSubtitle && trimmedLine === `**${fullLineSubtitle[1]}**`) {
flushParagraph();
elements.push({
type: 'subtitle',
content: fullLineSubtitle[1],
key: `subtitle-${keyIndex++}`
});
} else if (prefixedSubtitle && prefixedSubtitle[2].trim() !== '') {
flushParagraph();
elements.push({
type: 'subtitle',
content: prefixedSubtitle[1],
key: `subtitle-${keyIndex++}`
});
if (prefixedSubtitle[2].trim()) {
currentParagraph.push(prefixedSubtitle[2].trim());
}
} else if (trimmedLine === '') {
flushParagraph();
} else {
currentParagraph.push(line);
}
});
flushParagraph();
return elements;
};
if (isLoading) {
return (
Loading...
);
}
if (error || !article) {
return (
Article not found
router.back()}>
Go Back
);
}
const bodyElements = articleData.subtopics && articleData.subtopics.length > 0
? null
: parseBodyContent(cleanedBody);
const outletData = outlet as any;
const outletName = outletData?.name || articleData?.outletName || 'Unknown Outlet';
return (
{/* Header */}
{outletData?.avatar ? (
) : (
{getInitials(outletName)}
)}
{outletName}
setShowLanguage(true)}>
{/* Hero Image */}
{articleData.thumbnail && (
setImageLoaded(true)}
/>
{!imageLoaded && (
)}
{articleData.category && (
{articleData.category}
)}
{articleData.timeAgo && (
{articleData.timeAgo}
)}
)}
{/* Article Content Card */}
{/* Title */}
{articleData.title}
{/* Summary */}
{articleData.summary && (
{articleData.summary}
)}
{/* Body - Subtopics or Parsed Content */}
{articleData.subtopics && articleData.subtopics.length > 0 ? (
articleData.subtopics.map((topic: any, index: number) => (
{topic.title}
{Array.isArray(topic.content) ? (
topic.content.map((paragraph: string, pIndex: number) => (
{paragraph}
))
) : (
{topic.content}
)}
))
) : (
bodyElements?.map((element) => (
element.type === 'subtitle' ? (
{element.content}
) : (
{element.content}
)
))
)}
{/* Latest Articles Feed */}
{latestArticles?.items && latestArticles.items.length > 0 && (
Latest Articles
{latestArticles.items
.filter((item: any) => item.newsId && item.newsId !== id)
.slice(0, 10)
.map((item: any) => (
{
router.push(`/article/${newsId}`);
}}
isAd={item.isAd || false}
tags={item.tags || []}
/>
))}
)}
{/* Footer Action Bar */}
router.back()}>
setShowComments(true)}>
{/* Language Modal */}
setShowLanguage(false)}
currentLanguage={language}
onSelectLanguage={setLanguage}
/>
{/* Comments Drawer */}
setShowComments(false)}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#e0e0e0',
paddingVertical: 12,
paddingHorizontal: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
headerLeft: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
gap: 12,
},
outletAvatar: {
width: 40,
height: 40,
borderRadius: 20,
},
avatarFallback: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#e0e0e0',
justifyContent: 'center',
alignItems: 'center',
},
avatarText: {
fontSize: 14,
fontWeight: '600',
color: '#333',
},
outletInfo: {
flexDirection: 'column',
alignItems: 'flex-start',
gap: 3,
flex: 1,
},
miniLogo: {
height: 10,
width: 90,
resizeMode: 'contain',
marginLeft: 0,
},
outletName: {
fontSize: 16,
fontWeight: '600',
color: '#000',
maxWidth: 150,
},
headerRight: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
iconButton: {
padding: 8,
},
scrollView: {
flex: 1,
},
heroContainer: {
position: 'relative',
width: '100%',
aspectRatio: 16 / 9,
backgroundColor: '#e0e0e0',
},
heroImage: {
width: '100%',
height: '100%',
},
heroImageHidden: {
opacity: 0,
},
imagePlaceholder: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#e0e0e0',
},
categoryBadge: {
position: 'absolute',
top: 16,
left: 16,
backgroundColor: 'rgba(255, 255, 255, 0.95)',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
categoryText: {
fontSize: 12,
fontWeight: '600',
color: '#333',
},
timeContainer: {
position: 'absolute',
bottom: 12,
left: 12,
},
timeText: {
fontSize: 12,
color: '#fff',
fontWeight: '600',
textShadowColor: 'rgba(0, 0, 0, 0.75)',
textShadowOffset: { width: 0, height: 1 },
textShadowRadius: 3,
},
contentCard: {
backgroundColor: '#fff',
marginTop: 16,
marginHorizontal: 16,
marginBottom: 24,
padding: 20,
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#000',
marginBottom: 12,
lineHeight: 32,
},
summary: {
fontSize: 16,
color: '#666',
marginBottom: 20,
lineHeight: 24,
},
body: {
marginBottom: 16,
},
subtopicSection: {
marginBottom: 20,
},
subtitle: {
fontSize: 18,
fontWeight: '600',
color: '#000',
marginTop: 20,
marginBottom: 12,
},
paragraph: {
fontSize: 16,
color: '#333',
lineHeight: 26,
marginBottom: 16,
},
feedContainer: {
marginTop: 32,
marginHorizontal: 16,
marginBottom: 32,
},
feedTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#000',
marginBottom: 16,
},
feedItemWrapper: {
marginBottom: 12,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
text: {
fontSize: 14,
color: '#666',
marginTop: 10,
},
errorText: {
fontSize: 16,
color: 'red',
fontWeight: 'bold',
marginBottom: 16,
},
backButton: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
},
backButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
footer: {
backgroundColor: '#fff',
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
paddingVertical: 8,
paddingHorizontal: 8,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
footerButton: {
padding: 8,
width: 40,
alignItems: 'center',
},
footerActions: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
},
actionButton: {
padding: 8,
width: 36,
height: 36,
alignItems: 'center',
justifyContent: 'center',
},
});