import React, { memo } from 'react'; import { View, FlatList, ActivityIndicator, RefreshControl, StyleSheet, Image, TouchableOpacity, Platform } from 'react-native'; import { VideoPlayer } from '@/components/video/video-player'; import { ThemedText } from '@/components/themed-text'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { useColorScheme } from '@/hooks/use-color-scheme'; import { router } from 'expo-router'; import type { TemplateGeneration } from '@/lib/api/template-generations'; /** * 根据文件 URL 后缀名判断媒体类型 */ function getMediaType(url: string): 'image' | 'video' | 'unknown' { if (!url) return 'unknown'; const extension = url.split('.').pop()?.toLowerCase() || ''; const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg']; const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv', 'flv', 'm4v']; if (imageExts.includes(extension)) return 'image'; if (videoExts.includes(extension)) return 'video'; return 'unknown'; } export function ContentGallery({ generations, isRefreshing, onRefresh, isLoadingMore, hasMore, onLoadMore, ListHeaderComponent, }: { generations: TemplateGeneration[]; isRefreshing: boolean; onRefresh: () => void; isLoadingMore: boolean; hasMore: boolean; onLoadMore: () => void; ListHeaderComponent?: React.ComponentType | React.ReactElement | null; }) { const colorScheme = useColorScheme(); const palette = colorScheme === 'dark' ? darkPalette : lightPalette; const renderItem = ({ item }: { item: TemplateGeneration }) => ( ); const renderFooter = () => { if (isLoadingMore) { return ( 加载更多... ); } if (!hasMore && generations.length > 0) { return ( 没有更多内容了 ); } return null; }; return ( item.id} columnWrapperStyle={styles.columnWrapper} showsVerticalScrollIndicator={false} refreshControl={ } onEndReached={onLoadMore} onEndReachedThreshold={0.5} contentContainerStyle={styles.contentContainer} removeClippedSubviews={true} maxToRenderPerBatch={6} updateCellsBatchingPeriod={50} windowSize={10} /> ); } const ContentItem = memo(function ContentItem({ palette, generation, }: { palette: any; generation: TemplateGeneration; }) { const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', }); }; const renderMedia = () => { const mediaUrl = generation.resultUrl[0]; if (!mediaUrl) { return ( {generation.type === 'VIDEO' ? '🎬' : generation.type === 'IMAGE' ? '🖼️' : '📝'} ); } const mediaType = getMediaType(mediaUrl); if (mediaType === 'video') { if (Platform.OS === 'web') { return ( ); } else { return ( ); } } return ( ); }; return ( router.push(`/result?generationId=${generation.id}`)} activeOpacity={0.9} > {renderMedia()} {generation.template.title} {formatDate(generation.createdAt)} {generation.status !== 'completed' && ( {generation.status} )} ); }); const darkPalette = { background: '#050505', surface: '#121216', border: '#1D1E24', textPrimary: '#F6F7FA', textSecondary: '#8E9098', accent: '#B7FF2F', }; const lightPalette = { background: '#F7F8FB', surface: '#FFFFFF', border: '#E2E5ED', textPrimary: '#0F1320', textSecondary: '#5E6474', accent: '#405CFF', }; const styles = StyleSheet.create({ contentContainer: { paddingBottom: 120, paddingHorizontal: 12, }, columnWrapper: { justifyContent: 'space-between', }, contentItem: { flex: 1, borderRadius: 12, marginBottom: 12, overflow: 'hidden', marginHorizontal: 6, }, mediaContainer: { width: '100%', aspectRatio: 1, backgroundColor: '#000', }, placeholderContainer: { backgroundColor: '#1A1A1A', justifyContent: 'center', alignItems: 'center', }, placeholderText: { fontSize: 48, }, mediaImage: { width: '100%', height: '100%', }, mediaVideo: { width: '100%', height: '100%', }, contentInfo: { padding: 12, }, contentTitle: { fontSize: 15, fontWeight: '600', marginBottom: 8, }, contentMeta: { flexDirection: 'row', alignItems: 'center', }, metaItem: { flexDirection: 'row', alignItems: 'center', marginRight: 12, }, metaText: { fontSize: 12, marginLeft: 4, }, statusBadge: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 4, backgroundColor: 'rgba(0, 0, 0, 0.1)', }, statusText: { fontSize: 11, fontWeight: '600', textTransform: 'capitalize' as const, }, loadingMoreContainer: { paddingVertical: 20, alignItems: 'center', flexDirection: 'row', justifyContent: 'center', gap: 12, }, loadingMoreText: { fontSize: 14, }, noMoreContainer: { paddingVertical: 20, alignItems: 'center', }, noMoreText: { fontSize: 14, }, });