feat: enhance email deletion and spam handling with improved parameterization

This commit is contained in:
Linus Rath
2026-04-16 16:50:42 +02:00
parent f22699fe20
commit ad175d20e3
4 changed files with 62 additions and 31 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"WebFetch(domain:github.com)"
]
}
}
+13 -16
View File
@@ -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);
+1 -1
View File
@@ -176,7 +176,7 @@ export function EmailList({
setIsProcessing(true);
try {
await batchDelete(client);
await batchDelete(client, isInTrash);
} finally {
setTimeout(() => setIsProcessing(false), 500);
}
+41 -14
View File
@@ -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