feat: implement email sending functionality via Resend service

This commit is contained in:
beilunyang
2025-06-21 23:50:46 +08:00
parent 9d55564073
commit e85f6b04bd
27 changed files with 2347 additions and 467 deletions

View File

@@ -20,9 +20,13 @@ import {
interface Message {
id: string
from_address: string
from_address?: string
to_address?: string
subject: string
received_at: number
received_at?: number
sent_at?: number
content?: string
html?: string
}
interface MessageListProps {
@@ -30,8 +34,10 @@ interface MessageListProps {
id: string
address: string
}
onMessageSelect: (messageId: string | null) => void
messageType: 'received' | 'sent'
onMessageSelect: (messageId: string | null, messageType?: 'received' | 'sent') => void
selectedMessageId?: string | null
refreshTrigger?: number
}
interface MessageResponse {
@@ -40,7 +46,7 @@ interface MessageResponse {
total: number
}
export function MessageList({ email, onMessageSelect, selectedMessageId }: MessageListProps) {
export function MessageList({ email, messageType, onMessageSelect, selectedMessageId, refreshTrigger }: MessageListProps) {
const [messages, setMessages] = useState<Message[]>([])
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
@@ -60,6 +66,9 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
const fetchMessages = async (cursor?: string) => {
try {
const url = new URL(`/api/emails/${email.id}`, window.location.origin)
if (messageType === 'sent') {
url.searchParams.set('type', 'sent')
}
if (cursor) {
url.searchParams.set('cursor', cursor)
}
@@ -133,7 +142,7 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
const handleDelete = async (message: Message) => {
try {
const response = await fetch(`/api/emails/${email.id}/${message.id}`, {
const response = await fetch(`/api/emails/${email.id}/${message.id}${messageType === 'sent' ? '?type=sent' : ''}`, {
method: "DELETE"
})
@@ -184,6 +193,14 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [email.id])
useEffect(() => {
if (refreshTrigger && refreshTrigger > 0) {
setRefreshing(true)
fetchMessages()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refreshTrigger])
return (
<>
<div className="h-full flex flex-col">
@@ -210,7 +227,7 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
{messages.map(message => (
<div
key={message.id}
onClick={() => onMessageSelect(message.id)}
onClick={() => onMessageSelect(message.id, messageType)}
className={cn(
"p-3 hover:bg-primary/5 cursor-pointer group",
selectedMessageId === message.id && "bg-primary/10"
@@ -221,10 +238,12 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
<div className="min-w-0 flex-1">
<p className="font-medium text-sm truncate">{message.subject}</p>
<div className="mt-1 flex items-center gap-2 text-xs text-gray-500">
<span className="truncate">{message.from_address}</span>
<span className="truncate">
{message.from_address || message.to_address || ''}
</span>
<span className="flex items-center gap-1">
<Calendar className="w-3 h-3" />
{new Date(message.received_at).toLocaleString()}
{new Date(message.received_at || message.sent_at || 0).toLocaleString()}
</span>
</div>
</div>
@@ -250,7 +269,7 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
</div>
) : (
<div className="p-4 text-center text-sm text-gray-500">
{messageType === 'sent' ? '暂无发送的邮件' : '暂无收到的邮件'}
</div>
)}
</div>