import { useState, useEffect } from 'react' import { View, Text, StyleSheet, ScrollView, Dimensions, Pressable, TextInput, StatusBar as RNStatusBar, ActivityIndicator, Alert } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { Image } from 'expo-image' import { useRouter, useLocalSearchParams } from 'expo-router' import { LinearGradient } from 'expo-linear-gradient' import { LeftArrowIcon, UploadIcon, PointsIcon, WhitePointsIcon } from '@/components/icon' import UploadReferenceImageDrawer from '@/components/UploadReferenceImageDrawer' import { useTemplateActions } from '@/hooks/actions/use-template-actions' import { useFileUpload } from '@/hooks/actions/use-file-upload' import { useAigcTask } from '@/hooks/actions/use-aigc-task' import { useUserBalance } from '@/hooks/core/use-user-balance' import { useAuth } from '@/hooks/core/use-auth' const { width: screenWidth, height: screenHeight } = Dimensions.get('window') interface TemplateData { id: number | string videoUrl: any thumbnailUrl: any title: string duration: string creditsCost?: number } const CREDITS_COST = 10 export default function GenerateVideoScreen() { const router = useRouter() const params = useLocalSearchParams() const { user } = useAuth() const { balance, load: loadBalance } = useUserBalance() const [description, setDescription] = useState('') const [uploadedImage, setUploadedImage] = useState(null) const [uploadedImageUrl, setUploadedImageUrl] = useState(null) const [templateData, setTemplateData] = useState(null) const [drawerVisible, setDrawerVisible] = useState(false) const [isGenerating, setIsGenerating] = useState(false) const [generationProgress, setGenerationProgress] = useState('') const { uploadFile, loading: uploadLoading } = useFileUpload() const { submitTask, startPolling, stopPolling, isPolling } = useAigcTask() const { runTemplate, createGeneration, loading: actionLoading } = useTemplateActions() useEffect(() => { if (params.template && typeof params.template === 'string') { try { const parsed = JSON.parse(params.template) as TemplateData setTemplateData(parsed) if (parsed.thumbnailUrl) { setUploadedImage(parsed.thumbnailUrl) } } catch (error) { console.error('Failed to parse template data:', error) } } }, [params.template]) useEffect(() => { if (user?.id) { loadBalance(user.id) } }, [user?.id]) useEffect(() => { return () => { stopPolling() } }, []) const handleImageSelect = async (imageUri: any) => { setUploadedImage(imageUri) if (typeof imageUri === 'string' && (imageUri.startsWith('http://') || imageUri.startsWith('https://'))) { setUploadedImageUrl(imageUri) return } setGenerationProgress('上传图片中...') const { url, error } = await uploadFile(imageUri, 'templates') if (error) { Alert.alert('上传失败', error.message || '图片上传失败,请重试') setGenerationProgress('') return } if (url) { setUploadedImageUrl(url) setGenerationProgress('') } } const handleGenerateVideo = async () => { if (!templateData?.id) { Alert.alert('提示', '模板信息不完整') return } if (!uploadedImageUrl) { Alert.alert('提示', '请先上传参考图') return } if (balance < CREDITS_COST) { Alert.alert('积分不足', `生成视频需要 ${CREDITS_COST} 积分,当前积分:${balance}`) return } setIsGenerating(true) setGenerationProgress('提交生成任务...') const { taskId, error: submitError } = await submitTask({ model_name: 'video_generation', prompt: description || '生成视频', img_url: uploadedImageUrl, duration: templateData.duration || '3', resolution: '720p', watermark: false, webhook_flag: false, }) if (submitError) { Alert.alert('生成失败', submitError.message || '提交任务失败') setIsGenerating(false) setGenerationProgress('') return } if (!taskId) { Alert.alert('生成失败', '任务ID获取失败') setIsGenerating(false) setGenerationProgress('') return } setGenerationProgress('生成中...') startPolling( taskId, async (result) => { const videoUrls = result.data || [] const videoUrl = videoUrls[0] || null const { generation, error: createError } = await createGeneration({ templateId: String(templateData.id), type: 'VIDEO', resultUrl: videoUrls, originalUrl: uploadedImageUrl, status: 'completed', creditsCost: CREDITS_COST, }) if (createError) { Alert.alert('保存失败', createError.message || '生成记录保存失败') } else { Alert.alert('生成成功', '视频已生成完成', [ { text: '查看', onPress: () => { if (generation?.id) { router.push({ pathname: '/generationRecord' as any, params: { id: generation.id }, }) } }, }, { text: '继续生成' }, ]) if (user?.id) { loadBalance(user.id) } } setIsGenerating(false) setGenerationProgress('') }, (error) => { Alert.alert('生成失败', error.message || '视频生成失败') setIsGenerating(false) setGenerationProgress('') } ) } const isLoading = uploadLoading || actionLoading || isGenerating return ( {/* 顶部导航栏 */} router.back()}> {/* 标题区域 */} {templateData?.title} {templateData?.title} {uploadedImage ? ( ) : ( )} {/* 上传区域 */} { setDrawerVisible(true) }} > 上传参考图 {/* 描述输入区域 */} {/* 进度提示 */} {generationProgress ? ( {generationProgress} ) : null} {/* 底部生成按钮 */} {isLoading ? ( ) : ( <> 生成视频 {CREDITS_COST} )} setDrawerVisible(false)} onSelectImage={handleImageSelect} /> ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#090A0B', }, scrollView: { flex: 1, backgroundColor: '#090A0B', }, scrollContent: { flexGrow: 1, backgroundColor: '#090A0B', }, generateButtonContainer: { marginTop: 'auto', paddingTop: 12, }, content: { paddingHorizontal: 12, paddingTop: 16, paddingBottom: 16, backgroundColor: '#1C1E20', borderTopLeftRadius: 20, borderTopRightRadius: 20, gap: 8, minHeight: screenHeight - 59, }, header: { flexDirection: 'row', alignItems: 'center', paddingTop: 17, paddingHorizontal: 12, paddingBottom: 20, }, backButton: { width: 22, height: 22, alignItems: 'center', justifyContent: 'center', }, titleSection: { paddingHorizontal: 4, marginBottom: 11, }, title: { color: '#F5F5F5', fontSize: 16, fontWeight: '600', marginBottom: 5, marginLeft: -4, }, subtitle: { color: '#CCCCCC', fontSize: 12, }, uploadContainer: { height: 140, borderRadius: 12, backgroundColor: '#262A31', overflow: 'hidden', position: 'relative', alignItems: 'center', justifyContent: 'center', }, thumbnailContainer: { position: 'absolute', left: 8, bottom: 5, width: 56, height: 56, borderRadius: 8, overflow: 'hidden', borderWidth: 2, borderColor: '#FFFFFF', backgroundColor: '#090A0B', }, thumbnail: { width: '100%', height: '100%', }, avatarPlaceholder: { width: 56, height: 56, borderRadius: 28, backgroundColor: '#2F3134', alignItems: 'center', justifyContent: 'center', }, avatarCircle: { width: 40, height: 40, borderRadius: 20, backgroundColor: '#4A4C4F', }, uploadedImage: { width: '100%', height: '100%', }, uploadReferenceButton: { height: 110, backgroundColor: '#262A31', borderRadius: 12, alignItems: 'center', justifyContent: 'center', gap: 6, }, uploadReferenceText: { color: '#F5F5F5', fontSize: 12, fontWeight: '500', }, descriptionInput: { minHeight: 150, backgroundColor: '#262A31', borderRadius: 12, padding: 12, color: '#F5F5F5', fontSize: 14, lineHeight: 20, }, progressContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, paddingVertical: 8, }, progressText: { color: '#9966FF', fontSize: 14, fontWeight: '500', }, generateButton: { width: '100%', height: 48, backgroundColor: 'red', borderRadius: 12, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, }, generateButtonText: { color: '#F5F5F5', fontSize: 16, fontWeight: '500', }, pointsBadge: { flexDirection: 'row', alignItems: 'center', }, pointsText: { color: '#f5f5f5', fontSize: 14, fontWeight: '500', }, })