mirror of
https://github.com/beilunyang/moemail.git
synced 2026-09-05 23:36:37 +08:00
feat: add delete function for single email message
This commit is contained in:
@@ -5,6 +5,56 @@ import { and, eq } from "drizzle-orm"
|
|||||||
import { getUserId } from "@/lib/apiKey"
|
import { getUserId } from "@/lib/apiKey"
|
||||||
export const runtime = "edge"
|
export const runtime = "edge"
|
||||||
|
|
||||||
|
export async function DELETE(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ id: string; messageId: string }> }
|
||||||
|
) {
|
||||||
|
const userId = await getUserId()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const db = createDb()
|
||||||
|
const { id, messageId } = await params
|
||||||
|
const email = await db.query.emails.findFirst({
|
||||||
|
where: and(
|
||||||
|
eq(emails.id, id),
|
||||||
|
eq(emails.userId, userId!)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Email not found or no permission to view" },
|
||||||
|
{ status: 403 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await db.query.messages.findFirst({
|
||||||
|
where: and(
|
||||||
|
eq(messages.emailId, id),
|
||||||
|
eq(messages.id, messageId)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if(!message) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Message not found or already deleted" },
|
||||||
|
{ status: 404 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.delete(messages)
|
||||||
|
.where(eq(messages.id, messageId))
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to delete email:', error)
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Failed to delete message" },
|
||||||
|
{ status: 500 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(_request: Request, { params }: { params: Promise<{ id: string; messageId: string }> }) {
|
export async function GET(_request: Request, { params }: { params: Promise<{ id: string; messageId: string }> }) {
|
||||||
try {
|
try {
|
||||||
const { id, messageId } = await params
|
const { id, messageId } = await params
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react"
|
import { useState, useEffect, useRef } from "react"
|
||||||
import { Mail, Calendar, RefreshCw } from "lucide-react"
|
import {Mail, Calendar, RefreshCw, Trash2} from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { useThrottle } from "@/hooks/use-throttle"
|
import { useThrottle } from "@/hooks/use-throttle"
|
||||||
import { EMAIL_CONFIG } from "@/config"
|
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 Message {
|
interface Message {
|
||||||
id: string
|
id: string
|
||||||
@@ -19,7 +30,7 @@ interface MessageListProps {
|
|||||||
id: string
|
id: string
|
||||||
address: string
|
address: string
|
||||||
}
|
}
|
||||||
onMessageSelect: (messageId: string) => void
|
onMessageSelect: (messageId: string | null) => void
|
||||||
selectedMessageId?: string | null
|
selectedMessageId?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +49,8 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
const pollTimeoutRef = useRef<Timer>()
|
const pollTimeoutRef = useRef<Timer>()
|
||||||
const messagesRef = useRef<Message[]>([]) // 添加 ref 来追踪最新的消息列表
|
const messagesRef = useRef<Message[]>([]) // 添加 ref 来追踪最新的消息列表
|
||||||
const [total, setTotal] = useState(0)
|
const [total, setTotal] = useState(0)
|
||||||
|
const [messageToDelete, setMessageToDelete] = useState<Message | null>(null)
|
||||||
|
const { toast } = useToast()
|
||||||
|
|
||||||
// 当 messages 改变时更新 ref
|
// 当 messages 改变时更新 ref
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -118,6 +131,44 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
}
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
|
|
||||||
|
const handleDelete = async (message: Message) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/emails/${email.id}/${message.id}`, {
|
||||||
|
method: "DELETE"
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const data = await response.json()
|
||||||
|
toast({
|
||||||
|
title: "错误",
|
||||||
|
description: (data as { error: string }).error,
|
||||||
|
variant: "destructive"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setMessages(prev => prev.filter(e => e.id !== message.id))
|
||||||
|
setTotal(prev => prev - 1)
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "成功",
|
||||||
|
description: "邮件已删除"
|
||||||
|
})
|
||||||
|
|
||||||
|
if (selectedMessageId === message.id) {
|
||||||
|
onMessageSelect(null)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: "错误",
|
||||||
|
description: "删除邮件失败",
|
||||||
|
variant: "destructive"
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setMessageToDelete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!email.id) {
|
if (!email.id) {
|
||||||
return
|
return
|
||||||
@@ -134,6 +185,7 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
}, [email.id])
|
}, [email.id])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full flex flex-col">
|
||||||
<div className="p-2 flex justify-between items-center border-b border-primary/20">
|
<div className="p-2 flex justify-between items-center border-b border-primary/20">
|
||||||
<Button
|
<Button
|
||||||
@@ -160,7 +212,7 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
key={message.id}
|
key={message.id}
|
||||||
onClick={() => onMessageSelect(message.id)}
|
onClick={() => onMessageSelect(message.id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"p-3 hover:bg-primary/5 cursor-pointer",
|
"p-3 hover:bg-primary/5 cursor-pointer group",
|
||||||
selectedMessageId === message.id && "bg-primary/10"
|
selectedMessageId === message.id && "bg-primary/10"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -176,6 +228,17 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="opacity-0 group-hover:opacity-100 h-8 w-8"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setMessageToDelete(message)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 text-destructive" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -192,5 +255,25 @@ export function MessageList({ email, onMessageSelect, selectedMessageId }: Messa
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AlertDialog open={!!messageToDelete} onOpenChange={() => setMessageToDelete(null)}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>确认删除</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
确定要删除邮件 {messageToDelete?.subject} 吗?
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
className="bg-destructive hover:bg-destructive/90"
|
||||||
|
onClick={() => messageToDelete && handleDelete(messageToDelete)}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user