"use client" import { useState, useEffect, useRef } from "react" import { Mail, Calendar, RefreshCw } 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" interface Message { id: string from_address: string subject: string received_at: number } interface MessageListProps { email: { id: string address: string } onMessageSelect: (messageId: string) => void selectedMessageId?: string | null } interface MessageResponse { messages: Message[] nextCursor: string | null total: number } export function MessageList({ email, onMessageSelect, selectedMessageId }: MessageListProps) { const [messages, setMessages] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [nextCursor, setNextCursor] = useState(null) const [loadingMore, setLoadingMore] = useState(false) const pollTimeoutRef = useRef() const messagesRef = useRef([]) // 添加 ref 来追踪最新的消息列表 const [total, setTotal] = useState(0) // 当 messages 改变时更新 ref useEffect(() => { messagesRef.current = messages }, [messages]) const fetchMessages = async (cursor?: string) => { try { const url = new URL(`/api/emails/${email.id}`, window.location.origin) if (cursor) { url.searchParams.set('cursor', cursor) } const response = await fetch(url) const data = await response.json() as MessageResponse if (!cursor) { const newMessages = data.messages const oldMessages = messagesRef.current const lastDuplicateIndex = newMessages.findIndex( newMsg => oldMessages.some(oldMsg => oldMsg.id === newMsg.id) ) if (lastDuplicateIndex === -1) { setMessages(newMessages) setNextCursor(data.nextCursor) setTotal(data.total) return } const uniqueNewMessages = newMessages.slice(0, lastDuplicateIndex) setMessages([...uniqueNewMessages, ...oldMessages]) setTotal(data.total) return } setMessages(prev => [...prev, ...data.messages]) setNextCursor(data.nextCursor) setTotal(data.total) } catch (error) { console.error("Failed to fetch messages:", error) } finally { setLoading(false) setRefreshing(false) setLoadingMore(false) } } const startPolling = () => { stopPolling() // 先清除之前的轮询 pollTimeoutRef.current = setInterval(() => { if (!refreshing && !loadingMore) { fetchMessages() } }, EMAIL_CONFIG.POLL_INTERVAL) } const stopPolling = () => { if (pollTimeoutRef.current) { clearInterval(pollTimeoutRef.current) pollTimeoutRef.current = undefined } } const handleRefresh = async () => { setRefreshing(true) await fetchMessages() } 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) fetchMessages(nextCursor) } }, 200) useEffect(() => { if (!email.id) { return } setLoading(true) setNextCursor(null) fetchMessages() startPolling() return () => { stopPolling() } // eslint-disable-next-line react-hooks/exhaustive-deps }, [email.id]) return (
{total > 0 ? `${total} 封邮件` : "暂无邮件"}
{loading ? (
加载中...
) : messages.length > 0 ? (
{messages.map(message => (
onMessageSelect(message.id)} className={cn( "p-3 hover:bg-primary/5 cursor-pointer", selectedMessageId === message.id && "bg-primary/10" )} >

{message.subject}

{message.from_address} {new Date(message.received_at).toLocaleString()}
))} {loadingMore && (
加载更多...
)}
) : (
暂无邮件
)}
) }