feat(frontend): adapt message list logic for Gmail API compatibility

This commit is contained in:
rustmailer
2025-10-05 15:50:21 +08:00
parent 1e37ad2eb1
commit e6d35fc808
9 changed files with 238 additions and 105 deletions
+7 -2
View File
@@ -122,8 +122,13 @@ export const move_messages = async (accountId: number, payload: Record<string, a
return response.data;
};
export const get_full_message = async (accountId: number, mailbox: string, uid: number, subject: string) => {
const response = await axiosInstance.get(`/api/v1/full-message/${accountId}?mailbox=${mailbox}&uid=${uid}`, { responseType: 'blob' });
export const copy_messages = async (accountId: number, payload: Record<string, any>) => {
const response = await axiosInstance.post(`/api/v1/copy-messages/${accountId}`, payload);
return response.data;
};
export const get_full_message = async (accountId: number, mailbox: string, id: string, subject: string) => {
const response = await axiosInstance.get(`/api/v1/raw-message/${accountId}?mailbox=${mailbox}&id=${id}`, { responseType: 'blob' });
const blob = new Blob([response.data]);
let filename = subject.replace(/\.eml$/i, '');
saveAs(blob, `${filename}.eml`);
@@ -14,14 +14,14 @@ import { delete_messages } from '@/api/mailbox/envelope/api'
interface Props {
open: boolean
onOpenChange: (open: boolean) => void
deleteUids: number[],
setDeleteUids: React.Dispatch<React.SetStateAction<number[]>>;
deleteIds: string[],
setDeleteIds: React.Dispatch<React.SetStateAction<string[]>>;
accountId?: number,
mailbox?: string,
selectedUids: number[],
selectedIds: string[],
}
export function EnvelopeDeleteDialog({ open, onOpenChange, deleteUids, setDeleteUids, accountId, mailbox, selectedUids }: Props) {
export function EnvelopeDeleteDialog({ open, onOpenChange, deleteIds, setDeleteIds, accountId, mailbox, selectedIds }: Props) {
const queryClient = useQueryClient();
const deleteMutation = useMutation({
@@ -30,7 +30,7 @@ export function EnvelopeDeleteDialog({ open, onOpenChange, deleteUids, setDelete
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['mailbox-list-messages', accountId, mailbox] });
onOpenChange(false);
setDeleteUids([])
setDeleteIds([])
toast({
title: 'Messages deleted successfully',
description: 'The messages have been deleted.',
@@ -48,7 +48,7 @@ export function EnvelopeDeleteDialog({ open, onOpenChange, deleteUids, setDelete
const handleDelete = () => {
if (accountId && mailbox) {
let payload = {
uids: deleteUids.length > 0 ? deleteUids : selectedUids,
ids: deleteIds.length > 0 ? deleteIds : selectedIds,
mailbox
};
deleteMutation.mutate({ accountId, payload })
@@ -79,7 +79,7 @@ export function EnvelopeDeleteDialog({ open, onOpenChange, deleteUids, setDelete
Are you sure you want to move{' '}
<span className='font-bold'>
{(() => {
const emailCount = deleteUids.length > 0 ? deleteUids.length : selectedUids.length;
const emailCount = deleteIds.length > 0 ? deleteIds.length : selectedIds.length;
return emailCount > 1 ? `this ${emailCount} emails` : 'this email';
})()}
</span>{' '}
@@ -29,6 +29,8 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { MailboxData } from '@/api/mailbox/api'
import { AxiosError } from 'axios'
import { EmailAction, EmailActionDialog } from './email-action-dialog'
import { useMoveMessageMutation } from '@/hooks/use-move-messages'
import { useCopyMessageMutation } from '@/hooks/use-copy-messages'
interface MultilinesProps {
title: string,
@@ -69,11 +71,11 @@ interface Props {
currentEnvelope?: EmailEnvelope | undefined
currentMailbox?: MailboxData | undefined
currentAccountId?: number | undefined
setDeleteUids: React.Dispatch<React.SetStateAction<number[]>>;
setDeleteIds: React.Dispatch<React.SetStateAction<string[]>>;
setOpen: (str: MailboxDialogType | null) => void
}
export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope, setDeleteUids, currentMailbox, currentAccountId }: Props) {
export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope, setDeleteIds, currentMailbox, currentAccountId }: Props) {
const [downloadingAttachmentId, setDownloadingAttachmentId] = useState<string | null>(null);
const [content, setContent] = useState<string | null>(null);
@@ -86,6 +88,11 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
const [dialogAction, setDialogAction] = useState<EmailAction>('reply');
const { mutate: flagMessage } = useFlagMessageMutation();
const { mutate: moveMessage } = useMoveMessageMutation();
const { mutate: copyMessage } = useCopyMessageMutation();
const isGmailApi = currentEnvelope?.mid && currentEnvelope?.uid === 0;
const downloadMutation = useMutation({
mutationFn: ({ accountId, fileName, payload }: { accountId: number, fileName: string | undefined, payload: Record<string, any> }) => download_attachment(accountId, fileName, payload),
retry: false,
@@ -135,8 +142,9 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
const onDownload = (attachment: Attachment) => {
if (currentEnvelope) {
let payload = {
uid: currentEnvelope.uid,
id: isGmailApi ? currentEnvelope.mid! : currentEnvelope.uid.toString(),
mailbox: currentMailbox?.name,
attachment
};
@@ -210,33 +218,51 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
const onMarkAsRead = () => {
if (currentEnvelope) {
let payload = {
uids: [currentEnvelope.uid],
mailbox: currentMailbox?.name,
action: {
add: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: currentAccountId!, payload })
if (isGmailApi) {
let payload = {
ids: [currentEnvelope.mid!],
current_mailbox: "UNREAD",
target_mailbox: currentMailbox?.name
};
moveMessage({ accountId: currentAccountId!, payload })
} else {
let payload = {
uids: [currentEnvelope.uid],
mailbox: currentMailbox?.name,
action: {
add: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: currentAccountId!, payload })
}
}
}
const onMarkAsUnread = () => {
if (currentEnvelope) {
let payload = {
uids: [currentEnvelope.uid],
mailbox: currentMailbox?.name,
action: {
remove: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: currentAccountId!, payload })
if (isGmailApi) {
let payload = {
ids: [currentEnvelope.mid!],
current_mailbox: currentMailbox?.name,
target_mailbox: "UNREAD"
};
copyMessage({ accountId: currentAccountId!, payload })
} else {
let payload = {
uids: [currentEnvelope.uid],
mailbox: currentMailbox?.name,
action: {
remove: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: currentAccountId!, payload })
}
}
}
const handleDelete = () => {
if (currentEnvelope) {
setDeleteUids([currentEnvelope.uid])
isGmailApi ? setDeleteIds([currentEnvelope.mid!]) : setDeleteIds([currentEnvelope.uid.toString()])
setOpen('move-to-trash')
}
}
@@ -294,7 +320,7 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
async function downloadEmlFile() {
const filename = currentEnvelope?.subject || 'email_message';
if (!currentAccountId || !currentEnvelope?.mailbox_name || !currentEnvelope?.uid) {
if (!currentAccountId || !currentEnvelope?.mailbox_name || !currentEnvelope) {
toast({
title: 'Download failed',
description: 'Cannot download - missing required message information',
@@ -310,7 +336,7 @@ export function MailDisplayDrawer({ open, setOpen, onOpenChange, currentEnvelope
});
// Fetch the message
await get_full_message(currentAccountId!, currentEnvelope?.mailbox_name, currentEnvelope?.uid, filename);
await get_full_message(currentAccountId!, currentEnvelope?.mailbox_name, isGmailApi ? currentEnvelope?.mid! : currentEnvelope?.uid.toString(), filename);
// Optional: Show download complete notification
toast({
title: 'Download complete',
@@ -19,31 +19,37 @@ interface MailListProps {
currentEnvelope: EmailEnvelope | undefined
onEnvelopeChanged: (envelope: EmailEnvelope) => void
setOpen: (str: MailboxDialogType | null) => void,
setSelectedUids: React.Dispatch<React.SetStateAction<number[]>>;
setDeleteUids: React.Dispatch<React.SetStateAction<number[]>>;
selectedUids: number[];
setSelectedIds: React.Dispatch<React.SetStateAction<string[]>>;
setDeleteIds: React.Dispatch<React.SetStateAction<string[]>>;
selectedIds: string[];
}
export function MailList({
setOpen,
items,
currentEnvelope,
setDeleteUids,
setDeleteIds,
isLoading,
onEnvelopeChanged,
setSelectedUids,
selectedUids
setSelectedIds,
selectedIds
}: MailListProps) {
const handleDelete = (envelope: EmailEnvelope) => {
setDeleteUids([envelope.uid]);
if (envelope.mid && envelope.uid === 0) {
setDeleteIds([envelope.mid!])
} else {
setDeleteIds([envelope.uid.toString()])
}
setOpen("move-to-trash");
}
const handleCheckboxChange = (value: boolean | 'indeterminate', uid: number) => {
const isGmailApi = items.some(item => item.mid && item.uid === 0);
const handleCheckboxChange = (value: boolean | 'indeterminate', id: string) => {
if (value === true) {
setSelectedUids((prev) => [...prev, uid]);
setSelectedIds((prev) => [...prev, id]);
} else if (value === false) {
setSelectedUids((prev) => prev.filter((id) => id !== uid));
setSelectedIds((prev) => prev.filter((x) => x !== id));
}
};
@@ -82,14 +88,18 @@ export function MailList({
"flex flex-col gap-1.5 p-2 rounded-lg border transition-all cursor-pointer",
"hover:bg-accent/50",
currentEnvelope?.uid === item.uid && "bg-accent",
selectedUids.includes(item.uid) && "bg-primary/5"
(isGmailApi ? selectedIds.includes(item.mid!) : selectedIds.includes(item.uid.toString())) && "bg-primary/5"
)}
onClick={() => onEnvelopeChanged(item)}
>
<div className="flex items-center gap-1.5">
<Checkbox
checked={selectedUids.includes(item.uid)}
onCheckedChange={(checked) => handleCheckboxChange(checked, item.uid)}
checked={
isGmailApi ? selectedIds.includes(item.mid!) : selectedIds.includes(item.uid.toString())
}
onCheckedChange={(checked) => {
isGmailApi ? handleCheckboxChange(checked, item.mid!) : handleCheckboxChange(checked, item.uid.toString())
}}
onClick={(e) => e.stopPropagation()}
className="h-4 w-3 shrink-0"
/>
+86 -54
View File
@@ -41,6 +41,9 @@ import { useFlagMessageMutation } from "@/hooks/use-flag-messages"
import { EnvelopeFilterDialog } from "./envelope-filter-dialog"
import Logo from '@/assets/logo.svg'
import { PaginatedResponse } from "@/api"
import { useMoveMessageMutation } from "@/hooks/use-move-messages"
import { useCopyMessageMutation } from "@/hooks/use-copy-messages"
import { number } from "zod"
interface MailProps {
defaultLayout: number[] | undefined
@@ -176,12 +179,15 @@ export function Mail({
const [page, setPage] = React.useState(0);
const [pageSize, setPageSize] = React.useState(10);
const [selectedUids, setSelectedUids] = React.useState<number[]>([]);
const [deleteUids, setDeleteUids] = React.useState<number[]>([]);
const [selectedIds, setSelectedIds] = React.useState<string[]>([]);
const [deleteIds, setDeleteIds] = React.useState<string[]>([]);
const [currentFilter, setCurrentFilter] = React.useState<FilterForm | undefined>(undefined);
const [isSearching, setIsSearching] = React.useState(false);
// const [customFlags, setCustomFlags] = React.useState<string[]>([]);
const { mutate: flagMessage } = useFlagMessageMutation();
const { mutate: moveMessage } = useMoveMessageMutation();
const { mutate: copyMessage } = useCopyMessageMutation();
const queryClient = useQueryClient();
const { data: mailboxes, isLoading: isMailboxesLoading } = useQuery({
@@ -191,6 +197,8 @@ export function Mail({
})
const isGmailApi = envelopes?.items.some(item => item.mid && item.uid === 0);
React.useEffect(() => {
if (!selectedAccountId || !selectedMailbox) {
return;
@@ -245,7 +253,7 @@ export function Mail({
try {
setPage(0);
setPageSize(10);
setSelectedUids([]);
setSelectedIds([]);
setCurrentFilter(data);
const payload = {
@@ -271,48 +279,66 @@ export function Mail({
setCurrentFilter(undefined);
setPage(0);
setPageSize(10);
setSelectedUids([]);
setSelectedIds([]);
}
const hasNextPage = () => {
return !!pageTokenMapRef.current[page + 1];
}
const handlePageChange = (newPage: number) => {
setPage(newPage);
setSelectedUids([]);
setSelectedIds([]);
}
const handlePageSizeChange = (newSize: number) => {
setPage(0);
setPageSize(newSize);
setSelectedUids([]);
setSelectedIds([]);
}
const handleMarkFolderRead = () => {
if (selectedAccountId && selectedMailbox) {
let payload = {
uids: selectedUids,
mailbox: selectedMailbox?.name,
action: {
add: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: selectedAccountId, payload })
if (isGmailApi) {
let payload = {
ids: selectedIds,
current_mailbox: "UNREAD",
target_mailbox: selectedMailbox?.name
};
moveMessage({ accountId: selectedAccountId, payload })
} else {
let payload = {
uids: selectedIds.map(id => Number(id)),
mailbox: selectedMailbox?.name,
action: {
add: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: selectedAccountId, payload })
}
}
}
const handleMarkFolderUnread = () => {
if (selectedAccountId && selectedMailbox) {
let payload = {
uids: selectedUids,
mailbox: selectedMailbox?.name,
action: {
remove: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: selectedAccountId, payload })
if (isGmailApi) {
let payload = {
ids: selectedIds,
current_mailbox: selectedMailbox?.name,
target_mailbox: "UNREAD"
};
copyMessage({ accountId: selectedAccountId, payload })
} else {
let payload = {
uids: selectedIds.map(id => Number(id)),
mailbox: selectedMailbox?.name,
action: {
remove: [{ flag: "Seen" }]
}
};
flagMessage({ accountId: selectedAccountId, payload })
}
}
}
@@ -327,7 +353,7 @@ export function Mail({
}, [isError, error]);
return (
<MailboxProvider value={{ open, setOpen, currentMailbox: selectedMailbox, setCurrentMailbox: setSelectedMailbox, currentEnvelope: selectedEvelope, setCurrentEnvelope: setSelectedEvelope, deleteUids, setDeleteUids }}>
<MailboxProvider value={{ open, setOpen, currentMailbox: selectedMailbox, setCurrentMailbox: setSelectedMailbox, currentEnvelope: selectedEvelope, setCurrentEnvelope: setSelectedEvelope, deleteIds, setDeleteIds }}>
<TooltipProvider delayDuration={0}>
<ResizablePanelGroup
direction="horizontal"
@@ -377,7 +403,7 @@ export function Mail({
localStorage.setItem('mailbox:selectedAccountId', `${accountId}`);
setSelectedAccountId(accountId);
setSelectedMailbox(undefined);
setSelectedUids([]);
setSelectedIds([]);
}} defaultAccountId={lastSelectedAccountId} />
</div>
<Separator className="mt-2" />
@@ -407,7 +433,7 @@ export function Mail({
onSelectChange={(item) => {
if (item) {
setSelectedMailbox(mailboxes?.find(m => m.id === parseInt(item.id, 10)))
setSelectedUids([]);
setSelectedIds([]);
setPage(0);
} else {
setSelectedMailbox(undefined)
@@ -425,20 +451,20 @@ export function Mail({
<h2 className="text-xl font-bold cursor-pointer hover:underline" onClick={() => setOpen("mailbox")}>
{selectedMailbox?.name}
</h2>
{selectedUids.length > 0 && <Dot className="ml-2 text-sm text-muted-foreground" />}
{selectedUids.length > 0 && <div className="ml-2 text-sm text-muted-foreground">
{selectedUids.length} {selectedUids.length > 1 ? 'emails' : 'email'} selected
{selectedIds.length > 0 && <Dot className="ml-2 text-sm text-muted-foreground" />}
{selectedIds.length > 0 && <div className="ml-2 text-sm text-muted-foreground">
{selectedIds.length} {selectedIds.length > 1 ? 'emails' : 'email'} selected
</div>}
</div>
<Separator />
<div className="flex flex-wrap items-center gap-2 px-4 py-2">
<div className="ml-3 w-10">
{selectedUids.length > 0 ? (
{selectedIds.length > 0 ? (
<div
className="peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
onClick={(e) => {
e.stopPropagation();
setSelectedUids([]);
setSelectedIds([]);
}}
>
<div className="flex items-center justify-center h-full">
@@ -450,7 +476,11 @@ export function Mail({
className="peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
onClick={(e) => {
e.stopPropagation();
setSelectedUids(envelopes?.items.map((e) => e.uid) ?? []);
if (isGmailApi) {
setSelectedIds(envelopes?.items.map((e) => e.mid!) ?? []);
} else {
setSelectedIds(envelopes?.items.map((e) => e.uid.toString()) ?? []);
}
}}
/>
)}
@@ -465,7 +495,7 @@ export function Mail({
Clear Filters
</Button>
)}
{selectedUids.length > 0 && (
{selectedIds.length > 0 && (
<>
<Separator orientation="vertical" className="mx-1 h-6" />
<MoveTo
@@ -474,26 +504,28 @@ export function Mail({
accountId={selectedAccountId!}
mailbox={selectedMailbox?.name}
triggerUpdate={triggerUpdate}
selectedUids={selectedUids}
setSelectedUids={setSelectedUids}
selectedIds={selectedIds}
setSelectedIds={setSelectedIds}
/>
<Separator orientation="vertical" className="mx-1 h-6" />
<Button variant="outline" className="flex items-center" onClick={() => setOpen('move-to-trash')}>
<Trash2 />
Delete
</Button>
<Separator orientation="vertical" className="mx-1 h-6" />
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="flex items-center">
<Flag />
Flag
</Button>
</PopoverTrigger>
<PopoverContent className="w-96" align="start">
<CustomFlagInput selectedAccountId={selectedAccountId} selectedMailbox={selectedMailbox} selectedUids={selectedUids} />
</PopoverContent>
</Popover>
{!isGmailApi && <>
<Separator orientation="vertical" className="mx-1 h-6" />
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="flex items-center">
<Flag />
Flag
</Button>
</PopoverTrigger>
<PopoverContent className="w-96" align="start">
<CustomFlagInput selectedAccountId={selectedAccountId} selectedMailbox={selectedMailbox} selectedUids={selectedIds.map(id => Number(id))} />
</PopoverContent>
</Popover>
</>}
<Separator orientation="vertical" className="mx-1 h-6" />
<Button variant="outline" className="flex items-center" onClick={handleMarkFolderRead}>
<MailOpen />
@@ -521,10 +553,10 @@ export function Mail({
return dateB - dateA;
})}
setOpen={setOpen}
setDeleteUids={setDeleteUids}
setDeleteIds={setDeleteIds}
currentEnvelope={selectedEvelope}
setSelectedUids={setSelectedUids}
selectedUids={selectedUids}
setSelectedIds={setSelectedIds}
selectedIds={selectedIds}
onEnvelopeChanged={(envelope) => {
setOpen('display');
setSelectedEvelope(envelope);
@@ -568,7 +600,7 @@ export function Mail({
open={open === 'display'}
setOpen={setOpen}
onOpenChange={() => setOpen('display')}
setDeleteUids={setDeleteUids}
setDeleteIds={setDeleteIds}
currentEnvelope={selectedEvelope}
currentMailbox={selectedMailbox}
currentAccountId={selectedAccountId}
@@ -577,11 +609,11 @@ export function Mail({
<EnvelopeDeleteDialog
key='envelope-move-to-trash'
open={open === 'move-to-trash'}
deleteUids={deleteUids}
setDeleteUids={setDeleteUids}
deleteIds={deleteIds}
setDeleteIds={setDeleteIds}
accountId={selectedAccountId}
mailbox={selectedMailbox?.name}
selectedUids={selectedUids}
selectedIds={selectedIds}
onOpenChange={() => setOpen('move-to-trash')}
/>
<EnvelopeFilterDialog
@@ -23,12 +23,12 @@ interface MoveToProps {
mailboxes?: MailboxData[];
accountId: number,
mailbox: string,
selectedUids: number[],
selectedIds: string[],
triggerUpdate: (mailbox: string) => void
setSelectedUids: React.Dispatch<React.SetStateAction<number[]>>;
setSelectedIds: React.Dispatch<React.SetStateAction<string[]>>;
}
export function MoveTo({ isMailboxesLoading, mailboxes, accountId, mailbox, selectedUids, setSelectedUids, triggerUpdate }: MoveToProps) {
export function MoveTo({ isMailboxesLoading, mailboxes, accountId, mailbox, selectedIds, setSelectedIds, triggerUpdate }: MoveToProps) {
const [open, setOpen] = useState(false);
const moveMutation = useMutation({
mutationFn: ({ accountId, payload }: { accountId: number, payload: Record<string, any> }) => move_messages(accountId, payload),
@@ -36,7 +36,7 @@ export function MoveTo({ isMailboxesLoading, mailboxes, accountId, mailbox, sele
onSuccess: () => {
triggerUpdate(mailbox);
setOpen(false);
setSelectedUids([])
setSelectedIds([])
toast({
title: 'Messages moved successfully',
description: 'The messages have been moved.',
@@ -54,7 +54,7 @@ export function MoveTo({ isMailboxesLoading, mailboxes, accountId, mailbox, sele
const handleMove = (target: string) => {
if (target && mailbox && accountId) {
const payload = {
uids: selectedUids,
ids: selectedIds,
current_mailbox: mailbox,
target_mailbox: target
}
+2 -2
View File
@@ -17,8 +17,8 @@ interface MailboxContextType {
currentEnvelope: EmailEnvelope | undefined
setCurrentMailbox: React.Dispatch<React.SetStateAction<MailboxData | undefined>>
setCurrentEnvelope: React.Dispatch<React.SetStateAction<EmailEnvelope | undefined>>
deleteUids: number[]
setDeleteUids: React.Dispatch<React.SetStateAction<number[]>>
deleteIds: string[]
setDeleteIds: React.Dispatch<React.SetStateAction<string[]>>
}
const MailboxContext = React.createContext<MailboxContextType | null>(null)
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright © 2025 rustmailer.com
* Licensed under RustMailer License Agreement v1.0
* Unauthorized use or distribution is prohibited.
*/
import { copy_messages } from '@/api/mailbox/envelope/api';
import { useMutation } from '@tanstack/react-query';
import { toast } from './use-toast';
// Define the custom hook
export function useCopyMessageMutation() {
return useMutation({
mutationFn: ({ accountId, payload }: { accountId: number; payload: Record<string, any> }) => copy_messages(accountId, payload),
retry: 0,
onSuccess: () => {
toast({
title: 'Message copied successfully',
description: 'The message has been copied.',
});
},
onError: (error: Error) => {
toast({
title: 'Failed to copy message',
description: `${error.message}`,
variant: 'destructive',
});
},
});
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright © 2025 rustmailer.com
* Licensed under RustMailer License Agreement v1.0
* Unauthorized use or distribution is prohibited.
*/
import { move_messages } from '@/api/mailbox/envelope/api';
import { useMutation } from '@tanstack/react-query';
import { toast } from './use-toast';
// Define the custom hook
export function useMoveMessageMutation() {
return useMutation({
mutationFn: ({ accountId, payload }: { accountId: number; payload: Record<string, any> }) => move_messages(accountId, payload),
retry: 0,
onSuccess: () => {
toast({
title: 'Message moved successfully',
description: 'The message has been moved.',
});
},
onError: (error: Error) => {
toast({
title: 'Failed to move message',
description: `${error.message}`,
variant: 'destructive',
});
},
});
}