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', }, });