feat: enhance email deletion and spam handling with improved parameterization
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"WebFetch(domain:github.com)"
|
||||
]
|
||||
}
|
||||
}
|
||||
+13
-16
@@ -758,8 +758,8 @@ export default function Home() {
|
||||
if (isMobile) setActiveView('viewer');
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!client || !selectedEmail) return;
|
||||
const handleDelete = async (emailToDelete: Email | null = selectedEmail) => {
|
||||
if (!client || !emailToDelete) return;
|
||||
|
||||
// Check if we're currently in the trash or junk folder
|
||||
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
|
||||
@@ -778,7 +778,7 @@ export default function Home() {
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await deleteEmail(client, selectedEmail.id, true);
|
||||
await deleteEmail(client, emailToDelete.id, true);
|
||||
} catch (error) {
|
||||
console.error("Failed to permanently delete email:", error);
|
||||
}
|
||||
@@ -787,7 +787,7 @@ export default function Home() {
|
||||
const trashMailbox = mailboxes.find(m => m.role === 'trash' && !m.isShared);
|
||||
if (trashMailbox) {
|
||||
try {
|
||||
await moveToMailbox(client, selectedEmail.id, trashMailbox.id);
|
||||
await moveToMailbox(client, emailToDelete.id, trashMailbox.id);
|
||||
} catch (error) {
|
||||
console.error("Failed to move email to trash:", error);
|
||||
}
|
||||
@@ -860,10 +860,10 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleMarkAsSpam = async () => {
|
||||
if (!client || !selectedEmail) return;
|
||||
const handleMarkAsSpam = async (emailToMark: Email | null = selectedEmail) => {
|
||||
if (!client || !emailToMark) return;
|
||||
|
||||
const emailId = selectedEmail.id;
|
||||
const emailId = emailToMark.id;
|
||||
|
||||
try {
|
||||
await markAsSpam(client, emailId);
|
||||
@@ -891,11 +891,11 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUndoSpam = async () => {
|
||||
if (!client || !selectedEmail) return;
|
||||
const handleUndoSpam = async (emailToRestore: Email | null = selectedEmail) => {
|
||||
if (!client || !emailToRestore) return;
|
||||
|
||||
try {
|
||||
await undoSpam(client, selectedEmail.id);
|
||||
await undoSpam(client, emailToRestore.id);
|
||||
|
||||
const toastInstance = (await import('sonner')).toast;
|
||||
toastInstance.success(t('email_viewer.spam.toast_not_spam_success'));
|
||||
@@ -1715,8 +1715,7 @@ export default function Home() {
|
||||
}
|
||||
}}
|
||||
onDelete={async (email) => {
|
||||
selectEmail(email);
|
||||
await handleDelete();
|
||||
await handleDelete(email);
|
||||
}}
|
||||
onArchive={async (email) => {
|
||||
await handleArchive(email);
|
||||
@@ -1730,12 +1729,10 @@ export default function Home() {
|
||||
}
|
||||
}}
|
||||
onMarkAsSpam={async (email) => {
|
||||
selectEmail(email);
|
||||
await handleMarkAsSpam();
|
||||
await handleMarkAsSpam(email);
|
||||
}}
|
||||
onUndoSpam={async (email) => {
|
||||
selectEmail(email);
|
||||
await handleUndoSpam();
|
||||
await handleUndoSpam(email);
|
||||
}}
|
||||
onEditDraft={(email) => {
|
||||
handleEditDraft(email);
|
||||
|
||||
@@ -176,7 +176,7 @@ export function EmailList({
|
||||
|
||||
setIsProcessing(true);
|
||||
try {
|
||||
await batchDelete(client);
|
||||
await batchDelete(client, isInTrash);
|
||||
} finally {
|
||||
setTimeout(() => setIsProcessing(false), 500);
|
||||
}
|
||||
|
||||
+41
-14
@@ -87,7 +87,7 @@ interface EmailStore {
|
||||
|
||||
// Batch operations
|
||||
batchMarkAsRead: (client: IJMAPClient, read: boolean) => Promise<void>;
|
||||
batchDelete: (client: IJMAPClient) => Promise<void>;
|
||||
batchDelete: (client: IJMAPClient, permanent?: boolean) => Promise<void>;
|
||||
batchMoveToMailbox: (client: IJMAPClient, mailboxId: string) => Promise<void>;
|
||||
|
||||
// Spam operations
|
||||
@@ -1044,32 +1044,59 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
batchDelete: async (client) => {
|
||||
const { selectedEmailIds, emails, mailboxes } = get();
|
||||
batchDelete: async (client, permanent = false) => {
|
||||
const { selectedEmailIds, emails, mailboxes, selectedMailbox } = get();
|
||||
if (selectedEmailIds.size === 0) return;
|
||||
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
const emailIdsArray = Array.from(selectedEmailIds);
|
||||
|
||||
if (get().isUnifiedView) {
|
||||
// Group emails by accountId for cross-account operations
|
||||
const emailsByAccount = new Map<string, string[]>();
|
||||
for (const emailId of emailIdsArray) {
|
||||
const email = emails.find(e => e.id === emailId);
|
||||
const acctId = email?.accountId || '__default__';
|
||||
if (!emailsByAccount.has(acctId)) emailsByAccount.set(acctId, []);
|
||||
emailsByAccount.get(acctId)!.push(emailId);
|
||||
}
|
||||
// Determine if the current folder forces permanent deletion.
|
||||
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
|
||||
const isInTrash = currentMailbox?.role === 'trash';
|
||||
const permanentlyDeleteJunk = useSettingsStore.getState().permanentlyDeleteJunk;
|
||||
const isInJunk = currentMailbox?.role === 'junk';
|
||||
const forceDestroy = permanent || isInTrash || (isInJunk && permanentlyDeleteJunk);
|
||||
|
||||
// Group emails by accountId (handles unified view and search results spanning accounts).
|
||||
const emailsByAccount = new Map<string, string[]>();
|
||||
for (const emailId of emailIdsArray) {
|
||||
const email = emails.find(e => e.id === emailId);
|
||||
const acctId = email?.accountId || '__default__';
|
||||
if (!emailsByAccount.has(acctId)) emailsByAccount.set(acctId, []);
|
||||
emailsByAccount.get(acctId)!.push(emailId);
|
||||
}
|
||||
|
||||
const getClient = (acctId: string) =>
|
||||
acctId === '__default__' ? client : useAuthStore.getState().getClientForAccount(acctId);
|
||||
|
||||
if (forceDestroy) {
|
||||
const promises = Array.from(emailsByAccount.entries()).map(async ([acctId, ids]) => {
|
||||
const acctClient = acctId === '__default__' ? client : useAuthStore.getState().getClientForAccount(acctId);
|
||||
const acctClient = getClient(acctId);
|
||||
if (!acctClient) return;
|
||||
await acctClient.batchDeleteEmails(ids);
|
||||
});
|
||||
await Promise.allSettled(promises);
|
||||
} else {
|
||||
await client.batchDeleteEmails(emailIdsArray);
|
||||
// Move to trash per account.
|
||||
const promises = Array.from(emailsByAccount.entries()).map(async ([acctId, ids]) => {
|
||||
const acctClient = getClient(acctId);
|
||||
if (!acctClient) return;
|
||||
const trashMailbox = mailboxes.find(mb => {
|
||||
if (mb.role !== 'trash') return false;
|
||||
if (acctId === '__default__') return !mb.isShared;
|
||||
return mb.accountId === acctId;
|
||||
});
|
||||
if (!trashMailbox) {
|
||||
// No trash available for this account — fall back to destroy so the action isn't silently dropped.
|
||||
await acctClient.batchDeleteEmails(ids);
|
||||
return;
|
||||
}
|
||||
const trashId = trashMailbox.originalId || trashMailbox.id;
|
||||
await acctClient.batchMoveEmails(ids, trashId, trashMailbox.accountId);
|
||||
});
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
|
||||
// Remove deleted emails from local state
|
||||
|
||||
Reference in New Issue
Block a user