import React, { useEffect, useState, useCallback, useRef } from 'react' import { View, Text, StyleSheet, Pressable, StatusBar as RNStatusBar, ActivityIndicator, ScrollView, Platform, } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter, useLocalSearchParams } from 'expo-router' import { useTranslation } from 'react-i18next' import { LeftArrowIcon } from '@/components/icon' import SearchResultsGrid from '@/components/SearchResultsGrid' import { DynamicForm, type FormSchema, type DynamicFormRef } from '@/components/DynamicForm' import { useTemplateActions, useTemplateDetail, useTemplateGenerations, type TemplateGeneration } from '@/hooks' import Toast from '@/components/ui/Toast' import UploadReferenceImageDrawer from '@/components/drawer/UploadReferenceImageDrawer' import { uploadFile } from '@/lib/uploadFile' const CARD_HEIGHTS = [214, 236, 200, 220, 210, 225] export default function TemplateDetailScreen() { const { t } = useTranslation() const router = useRouter() const params = useLocalSearchParams() const templateId = typeof params.id === 'string' ? params.id : undefined const { data: templateDetail, loading: templateLoading, error: templateError, execute: loadTemplateDetail } = useTemplateDetail() const { generations, loading: generationsLoading, execute: loadGenerations } = useTemplateGenerations() const { runTemplate } = useTemplateActions() const [formSchema, setFormSchema] = useState(null) const [drawerVisible, setDrawerVisible] = useState(false) const [currentNodeId, setCurrentNodeId] = useState(null) const dynamicFormRef = useRef(null) useEffect(() => { if (templateId) { loadTemplateDetail({ id: templateId }) loadGenerations({ templateId, page: 1, limit: 20 }) } }, [templateId, loadTemplateDetail, loadGenerations]) // Set formSchema when templateDetail is loaded useEffect(() => { if (templateDetail?.formSchema?.startNodes && templateDetail.formSchema.startNodes.length > 0) { setFormSchema(templateDetail.formSchema) } }, [templateDetail]) const handleStartCreating = useCallback(() => { // Navigate to generateVideo page if no form schema if (!templateDetail?.formSchema?.startNodes || templateDetail.formSchema.startNodes.length === 0) { router.push({ pathname: '/generateVideo', params: { template: JSON.stringify(templateDetail), }, } as any) } // If formSchema exists, the form is already visible inline }, [templateDetail, router]) const handleFormSubmit = useCallback(async (data: Record) => { if (!templateId) return { error: 'Template ID is required' } const result = await runTemplate({ templateId, data, }) if (result.generationId) { // Show success message and reload generations Toast.show({ title: t('templateDetail.generationStarted') || '生成已开始', }) // Reload generations to show the new one loadGenerations({ templateId, page: 1, limit: 20 }) } return result }, [templateId, runTemplate, t, loadGenerations]) const handleOpenDrawer = useCallback((nodeId: string) => { setCurrentNodeId(nodeId) setDrawerVisible(true) }, []) const handleCloseDrawer = useCallback(() => { setDrawerVisible(false) setCurrentNodeId(null) }, []) const handleSelectImage = useCallback(async (imageUri: string, mimeType?: string, fileName?: string) => { if (!currentNodeId) return try { const url = await uploadFile({ uri: imageUri, mimeType, fileName }) // 通过 ref 更新 DynamicForm 中的字段值 dynamicFormRef.current?.updateFieldValue(currentNodeId, url, imageUri) Toast.show({ title: t('templateDetail.uploadSuccess') || '上传成功', }) } catch (error) { console.error('Upload failed:', error) Toast.show({ title: t('templateDetail.uploadFailed') || '上传失败,请重试', }) } finally { handleCloseDrawer() } }, [currentNodeId, t, handleCloseDrawer]) // 直接使用 TemplateGeneration 数据,只添加必要的 height 字段 const displayData = generations.map((generation, index) => ({ ...generation, height: CARD_HEIGHTS[index % CARD_HEIGHTS.length], title: generation.template?.title || generation.template?.titleEn || '', image: generation.resultUrl?.[0] || generation.originalUrl || '', })) if (templateLoading && !templateDetail) { return ( ) } if (templateError && !templateDetail) { return ( 加载失败,请返回重试 router.back()}> 返回 ) } return ( {/* 顶部导航栏 */} router.back()} style={styles.backButton} > {templateDetail?.title || templateDetail?.titleEn || t('templateDetail.title')} {/* 标题区域 */} {templateDetail?.title || templateDetail?.titleEn || t('templateDetail.title')} {t('templateDetail.subtitle')} {/* Dynamic Form - Show inline if formSchema exists */} {formSchema && formSchema.startNodes && formSchema.startNodes.length > 0 ? ( {t('templateDetail.fillForm') || '填写表单'} ) : ( /* Start Creating Button - Only show if no formSchema */ {t('templateDetail.startCreating') || '开始创作'} )} {/* Generations Section */} {t('templateDetail.generations') || '作品列表'} {/* 加载更多指示器 */} {generationsLoading && generations.length > 0 && ( 加载中... )} {/* Upload Drawer - 移到最外层以确保从屏幕底部弹出 */} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#090A0B', }, centerContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, }, header: { flexDirection: 'row', alignItems: 'center', paddingTop: 12, paddingHorizontal: 12, paddingBottom: 24, }, backButton: { padding: 4, }, headerTitle: { flex: 1, color: '#F5F5F5', fontSize: 16, fontWeight: '600', marginLeft: 12, }, scrollView: { flex: 1, }, scrollContent: { paddingBottom: 20, }, titleSection: { paddingHorizontal: 12, paddingBottom: 24, }, mainTitle: { color: '#F5F5F5', fontSize: 20, fontWeight: '500', marginBottom: 4, }, subTitle: { color: '#ABABAB', fontSize: 14, fontWeight: '400', }, formSection: { paddingHorizontal: 12, paddingBottom: 24, }, startCreatingButton: { backgroundColor: '#FF6699', marginHorizontal: 12, paddingVertical: 16, borderRadius: 12, alignItems: 'center', marginBottom: 24, }, startCreatingButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, generationsSection: { paddingHorizontal: 12, }, sectionTitle: { color: '#F5F5F5', fontSize: 16, fontWeight: '600', marginBottom: 16, }, errorText: { color: '#FFFFFF', fontSize: 14, textAlign: 'center', }, retryButton: { backgroundColor: '#FF6699', paddingHorizontal: 24, paddingVertical: 12, borderRadius: 8, }, retryButtonText: { color: '#FFFFFF', fontSize: 14, fontWeight: '600', }, loadingMoreContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, paddingVertical: 16, }, loadingMoreText: { color: '#ABABAB', fontSize: 12, }, })