import { Ionicons } from '@expo/vector-icons' import { Block, ConfirmModal, Text, Toast, VideoBox } from '@share/components' import Img from '@share/components/Img' import { LinearGradient } from 'expo-linear-gradient' import { router } from 'expo-router' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Dimensions, ScrollView } from 'react-native' import { useTemplateActions } from '@/hooks/actions/use-template-actions' import { useAuth } from '@/hooks/core/use-auth' import { useUserBalance } from '@/hooks/core/use-user-balance' import { type TemplateGeneration, useTemplateGenerations } from '@/hooks/data/use-template-generations' const BACKGROUND_VIDEOS = [ 'https://cdn.roasmax.cn/material/b46f380532e14cf58dd350dbacc7c34a.mp4', 'https://cdn.roasmax.cn/material/992e6c5d940c42feb71c27e556b754c0.mp4', 'https://cdn.roasmax.cn/material/e4947477843f4067be7c37569a33d17b.mp4', ] function isVideoUrl(url: string) { return url.endsWith('.mp4') } export default function My() { const { user, isLoading: authLoading, signOut } = useAuth() const { data: generationsData, loading: generationsLoading, load: loadGenerations } = useTemplateGenerations() const { balance, load: loadBalance } = useUserBalance() const { batchDeleteGenerations, loading: deleteLoading } = useTemplateActions() const [activeFilter, setActiveFilter] = useState<'my_gen' | 'my_album'>('my_gen') const [bgVideo] = useState(() => BACKGROUND_VIDEOS[Math.floor(Math.random() * BACKGROUND_VIDEOS.length)]) const [isSelectionMode, setIsSelectionMode] = useState(false) const [selectedIds, setSelectedIds] = useState>(new Set()) useEffect(() => { if (user?.id) { loadGenerations({ userId: user.id }) loadBalance(user.id) } }, [user?.id]) const generations = useMemo(() => generationsData?.data || [], [generationsData]) const filteredPosts = activeFilter === 'my_gen' ? generations : generations.filter((g: TemplateGeneration) => g.status === 'completed') const toggleSelectionMode = useCallback(() => { setIsSelectionMode((v) => !v) setSelectedIds(new Set()) }, []) const handleItemClick = useCallback( (generation: any) => { if (isSelectionMode) { setSelectedIds((prev) => { const next = new Set(prev) if (next.has(generation.id)) next.delete(generation.id) else next.add(generation.id) return next }) } }, [isSelectionMode], ) const handleDelete = useCallback(async () => { if (selectedIds.size === 0) return const { success } = await batchDeleteGenerations(Array.from(selectedIds)) if (success && user?.id) { await loadGenerations({ userId: user.id }) setSelectedIds(new Set()) setIsSelectionMode(false) } }, [selectedIds, batchDeleteGenerations, user?.id, loadGenerations]) const selectAll = useCallback(() => { if (selectedIds.size === filteredPosts.length) setSelectedIds(new Set()) else setSelectedIds(new Set(filteredPosts.map((p: TemplateGeneration) => p.id))) }, [selectedIds.size, filteredPosts]) const handleLogout = useCallback(() => { Toast.showModal( { Toast.hideModal() await signOut() router.replace('/auth') }} onCancel={() => Toast.hideModal()} />, {}, ) }, [signOut]) const { width: screenWidth } = Dimensions.get('window') const itemWidth = Math.floor((screenWidth - 24 - 12 * 2) / 3) const renderBanner = useCallback( () => ( ), [bgVideo], ) const renderHeaderCard = useCallback(() => { const username = user?.name || user?.email || 'Guest' const avatarUrl = user?.image || 'https://image.pollinations.ai/prompt/cool%20anime%20boy%20avatar%20hoodie?seed=123&nologo=true' const uid = user?.id?.slice(-6) || '000000' const generationCount = generations.length const completedCount = generations.filter((g: TemplateGeneration) => g.status === 'completed').length return ( {username} UID: {uid} CREDITS: {balance} TOTAL {generationCount} COMPLETED {completedCount} CREDITS {balance} {balance > 100 && ( PRO )} ) }, [user, balance, generations]) const renderActions = useCallback( () => ( {[ { label: 'SHOP', color: '#4ADE80', icon: 'bag-outline' as const }, { label: 'SYNC', color: '#FFE500', icon: 'watch-outline' as const }, { label: 'PAY', color: '#e61e25', icon: 'card-outline' as const }, ].map(({ label, color, icon }) => ( {label} ))} ), [], ) const renderFilters = useCallback( () => ( {['我的生成', '我的专辑'].map((label) => { const target = label === '我的生成' ? 'my_gen' : 'my_album' const isActive = activeFilter === (target as 'my_gen' | 'my_album') return ( setActiveFilter(target as 'my_gen' | 'my_album')} className={`border-2 border-black px-[16px] py-[4px] ${isActive ? 'bg-black' : 'bg-white'} -skew-x-12`} > {label} ) })} {isSelectionMode ? '取消' : '管理'} ), [activeFilter, isSelectionMode, toggleSelectionMode], ) const renderGrid = useCallback(() => { if (filteredPosts.length === 0) { return ( {activeFilter === 'my_gen' ? '暂无生成作品' : '暂无收藏'} ) } return ( {filteredPosts.map((generation: TemplateGeneration) => { const isSelected = selectedIds.has(generation.id) const imageUrl = generation.resultUrl?.[0] || generation.originalUrl || '' const statusRank = generation.status === 'completed' ? 'S' : generation.status === 'processing' ? 'A' : 'B' return ( handleItemClick(generation)} className="relative"> {imageUrl ? ( isVideoUrl(imageUrl) ? ( ) : ( ) ) : ( )} {isSelected && } {isSelectionMode && ( )} {!isSelectionMode && ( {statusRank} )} {!isSelectionMode && ( {generation.type} {new Date(generation.createdAt).toLocaleDateString()} )} ) })} ) }, [filteredPosts, selectedIds, isSelectionMode, handleItemClick, activeFilter, itemWidth]) const renderSelection = useCallback(() => { if (!isSelectionMode) return null return ( 已选: {selectedIds.size} 全选 0 && !deleteLoading ? 'bg-[#e61e25]' : 'bg-gray-200'}`} > {deleteLoading ? ( ) : ( )} {deleteLoading ? '删除中...' : '删除'} ) }, [isSelectionMode, selectedIds.size, selectAll, handleDelete, deleteLoading]) if (authLoading || generationsLoading) { return ( {renderBanner()} 加载中... ) } return ( {renderBanner()} {renderHeaderCard()} {renderActions()} {renderFilters()} {renderGrid()} {renderSelection()} ) }