mirror of
https://github.com/rustmailer/rustmailer.git
synced 2026-08-19 00:01:07 +00:00
feat(frontend): adapt message list logic for Gmail API compatibility
This commit is contained in:
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -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',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user