"use client" import { useEffect, useState } from "react" import { useSession } from "next-auth/react" import { CreateDialog } from "./create-dialog" import { Mail, RefreshCw, Trash2 } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { useThrottle } from "@/hooks/use-throttle" import { EMAIL_CONFIG } from "@/config" import { useToast } from "@/components/ui/use-toast" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" interface Email { id: string address: string createdAt: number expiresAt: number } interface EmailListProps { onEmailSelect: (email: Email | null) => void selectedEmailId?: string } interface EmailResponse { emails: Email[] nextCursor: string | null total: number } export function EmailList({ onEmailSelect, selectedEmailId }: EmailListProps) { const { data: session } = useSession() const [emails, setEmails] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [nextCursor, setNextCursor] = useState(null) const [loadingMore, setLoadingMore] = useState(false) const [total, setTotal] = useState(0) const [emailToDelete, setEmailToDelete] = useState(null) const { toast } = useToast() const fetchEmails = async (cursor?: string) => { try { const url = new URL("/api/emails", window.location.origin) if (cursor) { url.searchParams.set('cursor', cursor) } const response = await fetch(url) const data = await response.json() as EmailResponse if (!cursor) { const newEmails = data.emails const oldEmails = emails const lastDuplicateIndex = newEmails.findIndex( newEmail => oldEmails.some(oldEmail => oldEmail.id === newEmail.id) ) if (lastDuplicateIndex === -1) { setEmails(newEmails) setNextCursor(data.nextCursor) setTotal(data.total) return } const uniqueNewEmails = newEmails.slice(0, lastDuplicateIndex) setEmails([...uniqueNewEmails, ...oldEmails]) setTotal(data.total) return } setEmails(prev => [...prev, ...data.emails]) setNextCursor(data.nextCursor) setTotal(data.total) } catch (error) { console.error("Failed to fetch emails:", error) } finally { setLoading(false) setRefreshing(false) setLoadingMore(false) } } const handleRefresh = async () => { setRefreshing(true) await fetchEmails() } const handleScroll = useThrottle((e: React.UIEvent) => { if (loadingMore) return const { scrollHeight, scrollTop, clientHeight } = e.currentTarget const threshold = clientHeight * 1.5 const remainingScroll = scrollHeight - scrollTop if (remainingScroll <= threshold && nextCursor) { setLoadingMore(true) fetchEmails(nextCursor) } }, 200) useEffect(() => { if (session) fetchEmails() // eslint-disable-next-line react-hooks/exhaustive-deps }, [session]) const handleDelete = async (email: Email) => { try { const response = await fetch(`/api/emails/${email.id}`, { method: "DELETE" }) if (!response.ok) { const data = await response.json() toast({ title: "错误", description: (data as { error: string }).error, variant: "destructive" }) return } setEmails(prev => prev.filter(e => e.id !== email.id)) setTotal(prev => prev - 1) toast({ title: "成功", description: "邮箱已删除" }) if (selectedEmailId === email.id) { onEmailSelect(null) } } catch { toast({ title: "错误", description: "删除邮箱失败", variant: "destructive" }) } finally { setEmailToDelete(null) } } if (!session) return null return ( <>
{total}/{EMAIL_CONFIG.MAX_ACTIVE_EMAILS} 个邮箱
{loading ? (
加载中...
) : emails.length > 0 ? (
{emails.map(email => (
onEmailSelect(email)} >
{email.address}
{new Date(email.expiresAt).getFullYear() === 9999 ? ( "永久有效" ) : ( `过期时间: ${new Date(email.expiresAt).toLocaleString()}` )}
))} {loadingMore && (
加载更多...
)}
) : (
还没有邮箱,创建一个吧!
)}
setEmailToDelete(null)}> 确认删除 确定要删除邮箱 {emailToDelete?.address} 吗?此操作将同时删除该邮箱中的所有邮件,且不可恢复。 取消 emailToDelete && handleDelete(emailToDelete)} > 删除 ) }