import { PageLayout } from '@/components/bestai/layout'; import { getTemplateGenerations, TemplateGeneration } from '@/lib/api/template-generations'; import { router } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Image, Platform, RefreshControl, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; const LAYOUT_CONFIG = { VIDEO_HEIGHT: 280, IMAGE_HEIGHT: 240, DEFAULT_HEIGHT: 200, COLUMN_GAP: 16, PAGE_SIZE: 20, LOAD_MORE_THRESHOLD: 200, SCROLL_THROTTLE: 800, } as const; interface GroupedData { [date: string]: TemplateGeneration[]; } /** * 根据文件 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'; } const groupByDate = (data: TemplateGeneration[]): GroupedData => { const groups: GroupedData = {}; data.forEach(item => { const date = new Date(item.createdAt); const dateKey = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'short' }); if (!groups[dateKey]) { groups[dateKey] = []; } groups[dateKey].push(item); }); return groups; }; const formatDateLabel = (dateStr: string): string => { const date = new Date(dateStr); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); const dateOnly = new Date(date.toDateString()); const todayOnly = new Date(today.toDateString()); const yesterdayOnly = new Date(yesterday.toDateString()); if (dateOnly.getTime() === todayOnly.getTime()) { return 'Today'; } else if (dateOnly.getTime() === yesterdayOnly.getTime()) { return 'Yesterday'; } else { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'short' }); } }; const distributeToColumns = (items: TemplateGeneration[]) => { const leftColumn: TemplateGeneration[] = []; const rightColumn: TemplateGeneration[] = []; let leftHeight = 0; let rightHeight = 0; items.forEach(item => { const height = item.type === 'VIDEO' ? LAYOUT_CONFIG.VIDEO_HEIGHT : LAYOUT_CONFIG.IMAGE_HEIGHT; if (leftHeight <= rightHeight) { leftColumn.push(item); leftHeight += height + LAYOUT_CONFIG.COLUMN_GAP; } else { rightColumn.push(item); rightHeight += height + LAYOUT_CONFIG.COLUMN_GAP; } }); return { leftColumn, rightColumn }; }; const getImageHeight = (type: string): number => { switch (type) { case 'VIDEO': return LAYOUT_CONFIG.VIDEO_HEIGHT; case 'IMAGE': return LAYOUT_CONFIG.IMAGE_HEIGHT; default: return LAYOUT_CONFIG.DEFAULT_HEIGHT; } }; const getStatusColor = (status: string): string => { switch (status) { case 'completed': return '#4CAF50'; case 'processing': case 'pending': return '#FFA726'; case 'failed': return '#EF5350'; default: return '#9E9E9E'; } }; const getStatusText = (status: string): string => { switch (status) { case 'completed': return '已完成'; case 'processing': case 'pending': return '处理中'; case 'failed': return '失败'; default: return status; } }; const MediaItem = React.memo(({ item }: { item: TemplateGeneration }) => { const url = item.resultUrl && item.resultUrl.length > 0 ? item.resultUrl[0] : null; if (!url) { return ( {item.type === 'VIDEO' ? '🎬' : item.type === 'IMAGE' ? '🖼️' : '📝'} ); } const mediaType = getMediaType(url); if (mediaType === 'video') { if (Platform.OS === 'web') { return (