'use client'; import { useState, useEffect, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import Image from 'next/image'; import { useTranslation } from 'react-i18next'; import { Article } from '@/lib/types'; import { formatDate } from '@/lib/utils'; import { Skeleton } from '@/app/components/ui/skeleton'; import { Button } from '@/app/components/ui/button'; import { Card, CardContent, CardFooter, CardHeader } from '@/app/components/ui/card'; import { Badge } from '@/app/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/app/components/ui/select'; import { Input } from '@/app/components/ui/input'; import { Search, Filter, Grid, List, Calendar, TrendingUp, Clock } from 'lucide-react'; import { useDebounce } from '@/hooks/useDebounce'; import { useInfiniteScroll } from '@/hooks/useInfiniteScroll'; interface NewsPageClientProps { locale: string; initialData?: { articles: Article[]; categories: string[]; } | null; } export default function NewsPageClient({ locale, initialData }: NewsPageClientProps) { const { t } = useTranslation('news'); const router = useRouter(); // 获取嵌入数据的优化版本 const getEmbeddedData = useCallback(() => { if (typeof window !== 'undefined') { try { const scriptElement = document.getElementById('initial-news-data'); if (scriptElement) { const data = JSON.parse(scriptElement.textContent || '{}'); console.log('[NewsPageClient] Found embedded data:', data); return data; } } catch (error) { console.error('Failed to parse embedded data:', error); } } return null; }, []); const effectiveInitialData = initialData || getEmbeddedData(); // 状态管理 const [articles, setArticles] = useState(effectiveInitialData?.articles || []); const [categories, setCategories] = useState(effectiveInitialData?.categories || []); const [selectedCategory, setSelectedCategory] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState<'date' | 'title' | 'category'>('date'); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [isLoading, setIsLoading] = useState(!effectiveInitialData); const [error, setError] = useState(null); // 搜索防抖 const debouncedSearchQuery = useDebounce(searchQuery, 300); // 检测静态模式 const isStaticMode = useCallback(() => { if (effectiveInitialData) return true; if (typeof window !== 'undefined') { if (window.location.protocol === 'file:') return true; const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' || window.location.hostname.includes('dev'); return !isDev; } return false; }, [effectiveInitialData]); // 获取文章数据的优化版本 const fetchArticles = useCallback(async () => { if (effectiveInitialData && selectedCategory === 'all') { console.log('[NewsPageClient] Using initial data directly'); return; } try { setIsLoading(true); setError(null); if (isStaticMode() && effectiveInitialData) { const filteredArticlesData = selectedCategory === 'all' ? effectiveInitialData.articles : effectiveInitialData.articles.filter((article: Article) => article.metadata.category === selectedCategory); setArticles(filteredArticlesData); setCategories(effectiveInitialData.categories); console.log(`[NewsPageClient] Filtered static data: ${filteredArticlesData.length} articles`); return; } if (isStaticMode()) { throw new Error('No static data available in static mode'); } const response = await fetch(`/api/articles?locale=${locale}&category=${selectedCategory}`); if (!response.ok) { throw new Error('Failed to fetch articles'); } const data = await response.json(); setArticles(data.articles); setCategories(data.categories); console.log(`[NewsPageClient] Loaded API data: ${data.articles.length} articles`); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); console.error('Error fetching articles:', err); } finally { setIsLoading(false); } }, [locale, selectedCategory, isStaticMode, effectiveInitialData]); // 初始化数据 useEffect(() => { if (effectiveInitialData && selectedCategory === 'all') { console.log(`[NewsPageClient] Initializing with ${effectiveInitialData.articles.length} articles`); setArticles(effectiveInitialData.articles); setCategories(effectiveInitialData.categories); setIsLoading(false); return; } fetchArticles(); }, [fetchArticles, effectiveInitialData, selectedCategory]); // 过滤和排序逻辑优化 const filteredAndSortedArticles = useMemo(() => { let filtered = articles.filter((article) => { const matchesSearch = article.metadata.title.toLowerCase().includes(debouncedSearchQuery.toLowerCase()) || article.metadata.description.toLowerCase().includes(debouncedSearchQuery.toLowerCase()) || article.metadata.tags.some((tag) => tag.toLowerCase().includes(debouncedSearchQuery.toLowerCase()) ); const matchesCategory = selectedCategory === 'all' || article.metadata.category === selectedCategory; return matchesSearch && matchesCategory; }); // 排序逻辑 filtered.sort((a, b) => { switch (sortBy) { case 'date': return new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime(); case 'title': return a.metadata.title.localeCompare(b.metadata.title); case 'category': return a.metadata.category.localeCompare(b.metadata.category); default: return 0; } }); return filtered; }, [articles, debouncedSearchQuery, selectedCategory, sortBy]); // 无限滚动 const { items: displayedArticles, loadMore, hasMore, isLoadingMore } = useInfiniteScroll(filteredAndSortedArticles, 9); // 事件处理器 const handleCategoryChange = (value: string) => { setSelectedCategory(value); }; const handleSortChange = (value: 'date' | 'title' | 'category') => { setSortBy(value); }; const handleSearch = (value: string) => { setSearchQuery(value); }; // 统计信息 const statsInfo = useMemo(() => { const totalArticles = articles.length; const filteredCount = filteredAndSortedArticles.length; const categoriesCount = categories.length; return { total: totalArticles, filtered: filteredCount, categories: categoriesCount }; }, [articles.length, filteredAndSortedArticles.length, categories.length]); // 渲染骨架屏 const renderSkeleton = () => (
{[...Array(9)].map((_, index) => (
))}
); // 渲染错误状态 const renderError = () => (

{t('error.title')}

{error}

); // 渲染空状态 const renderEmpty = () => (

{t('news.noArticles')}

{t('news.tryDifferentSearch')}

); // 渲染文章卡片 const renderArticleCard = (article: Article, index: number) => (
{article.metadata.image ? ( {article.metadata.title} ) : (
)}
{t(`categories.${article.metadata.category}`)}
{formatDate(article.metadata.date, locale)}

{article.metadata.title}

{article.metadata.description}

{article.metadata.tags.slice(0, 3).map((tag, index) => ( {tag} ))} {article.metadata.tags.length > 3 && ( +{article.metadata.tags.length - 3} )}
{viewMode === 'grid' && ( )}
); return (
{/* 页面头部 */}

{t('news.title')}

{t('news.latest')} • {statsInfo.filtered} / {statsInfo.total} 篇文章

{/* 搜索和过滤区域 */}
handleSearch(e.target.value)} className="pl-10 h-11" />
{/* 文章列表 */} {isLoading ? ( renderSkeleton() ) : error ? ( renderError() ) : filteredAndSortedArticles.length === 0 ? ( renderEmpty() ) : (
{displayedArticles.map((article, index) => renderArticleCard(article, index))}
{/* 无限滚动加载更多 */} {hasMore && (
)} {/* 底部统计信息 */} {!hasMore && displayedArticles.length > 0 && (
已显示全部 {displayedArticles.length} 篇文章
)}
)}
); }