"use client" import { useEffect, useState } from "react" import { useSession } from "next-auth/react" import { useTranslations } from "next-intl" import { CreateDialog } from "./create-dialog" import { ShareDialog } from "./share-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" import { ROLES } from "@/lib/permissions" import { useUserRole } from "@/hooks/use-user-role" import { useConfig } from "@/hooks/use-config" 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 { config } = useConfig() const { role } = useUserRole() const t = useTranslations("emails.list") const tCommon = useTranslations("common.actions") 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() }, [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: t("error"), description: (data as { error: string }).error, variant: "destructive" }) return } setEmails(prev => prev.filter(e => e.id !== email.id)) setTotal(prev => prev - 1) toast({ title: t("success"), description: t("deleteSuccess") }) if (selectedEmailId === email.id) { onEmailSelect(null) } } catch { toast({ title: t("error"), description: t("deleteFailed"), variant: "destructive" }) } finally { setEmailToDelete(null) } } if (!session) return null return ( <>
{role === ROLES.EMPEROR ? ( t("emailCountUnlimited", { count: total }) ) : ( t("emailCount", { count: total, max: config?.maxEmails || EMAIL_CONFIG.MAX_ACTIVE_EMAILS }) )}
{loading ? (
{t("loading")}
) : emails.length > 0 ? (
{emails.map(email => (
onEmailSelect(email)} >
{email.address}
{new Date(email.expiresAt).getFullYear() === 9999 ? ( t("permanent") ) : ( `${t("expiresAt")}: ${new Date(email.expiresAt).toLocaleString()}` )}
e.stopPropagation()}>
))} {loadingMore && (
{t("loadingMore")}
)}
) : (
{t("noEmails")}
)}
setEmailToDelete(null)}> {t("deleteConfirm")} {t("deleteDescription", { email: emailToDelete?.address || "" })} {tCommon("cancel")} emailToDelete && handleDelete(emailToDelete)} > {tCommon("delete")} ) }