fix: batch shortcuts act on multi-selection when present #228

This commit is contained in:
Linus Rath
2026-04-28 00:04:29 +02:00
parent 27451807db
commit 68f1fabc4b
2 changed files with 78 additions and 18 deletions
+68 -12
View File
@@ -171,6 +171,11 @@ export default function Home() {
createMailbox, createMailbox,
renameMailbox, renameMailbox,
deleteMailbox, deleteMailbox,
batchDelete,
batchArchive,
batchMarkAsRead,
batchMarkAsSpam,
batchUndoSpam,
} = useEmailStore(); } = useEmailStore();
const enableUnifiedMailbox = useSettingsStore((s) => s.enableUnifiedMailbox); const enableUnifiedMailbox = useSettingsStore((s) => s.enableUnifiedMailbox);
@@ -353,27 +358,77 @@ export default function Home() {
onToggleStar: () => { onToggleStar: () => {
if (selectedEmail) handleToggleStar(); if (selectedEmail) handleToggleStar();
}, },
onArchive: () => { onArchive: async () => {
if (selectedEmail) handleArchive(); if (selectedEmailIds.size > 0 && client) {
try {
await batchArchive(client);
} catch (error) {
console.error("Failed to batch archive:", error);
}
} else if (selectedEmail) {
handleArchive();
}
}, },
onDelete: () => { onDelete: async () => {
if (selectedEmail) handleDelete(); if (selectedEmailIds.size > 0 && client) {
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
const isInTrash = currentMailbox?.role === 'trash';
const isInJunk = currentMailbox?.role === 'junk';
const permanentlyDeleteJunk = useSettingsStore.getState().permanentlyDeleteJunk;
const permanent = isInTrash || (isInJunk && permanentlyDeleteJunk);
const confirmed = await confirmDialog({
title: permanent
? t('email_list.permanent_delete_confirm_title')
: t('email_list.batch_actions.delete_confirm_title'),
message: permanent
? t('email_list.permanent_delete_confirm_batch_message', { count: selectedEmailIds.size })
: t('email_list.batch_actions.delete_confirm_message', { count: selectedEmailIds.size }),
confirmText: permanent
? t('email_list.permanent_delete')
: t('email_list.batch_actions.delete'),
variant: "destructive",
});
if (!confirmed) return;
try {
await batchDelete(client, permanent);
} catch (error) {
console.error("Failed to batch delete:", error);
}
} else if (selectedEmail) {
handleDelete();
}
}, },
onMarkAsUnread: async () => { onMarkAsUnread: async () => {
if (selectedEmail && client) { if (!client) return;
if (selectedEmailIds.size > 0) {
await batchMarkAsRead(client, false);
} else if (selectedEmail) {
await markAsRead(client, selectedEmail.id, false); await markAsRead(client, selectedEmail.id, false);
} }
}, },
onMarkAsRead: async () => { onMarkAsRead: async () => {
if (selectedEmail && client) { if (!client) return;
if (selectedEmailIds.size > 0) {
await batchMarkAsRead(client, true);
} else if (selectedEmail) {
await markAsRead(client, selectedEmail.id, true); await markAsRead(client, selectedEmail.id, true);
} }
}, },
onToggleSpam: () => { onToggleSpam: async () => {
if (selectedEmail) { const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
// Check if we're in junk folder const isInJunk = currentMailbox?.role === 'junk';
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox); if (selectedEmailIds.size > 0 && client) {
const isInJunk = currentMailbox?.role === 'junk'; const ids = Array.from(selectedEmailIds);
try {
if (isInJunk) {
await batchUndoSpam(client, ids);
} else {
await batchMarkAsSpam(client, ids);
}
} catch (error) {
console.error("Failed to batch toggle spam:", error);
}
} else if (selectedEmail) {
if (isInJunk) { if (isInJunk) {
handleUndoSpam(); handleUndoSpam();
} else { } else {
@@ -408,13 +463,14 @@ export default function Home() {
clearSelection(); clearSelection();
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}), [emails, selectedEmail, client, selectedMailbox, isMobile, isTablet]); }), [emails, selectedEmail, client, selectedMailbox, isMobile, isTablet, selectedEmailIds, mailboxes]);
// Initialize keyboard shortcuts // Initialize keyboard shortcuts
useKeyboardShortcuts({ useKeyboardShortcuts({
enabled: isAuthenticated && !showComposer, enabled: isAuthenticated && !showComposer,
emails, emails,
selectedEmailId: selectedEmail?.id, selectedEmailId: selectedEmail?.id,
selectionCount: selectedEmailIds.size,
handlers: keyboardHandlers, handlers: keyboardHandlers,
}); });
+10 -6
View File
@@ -39,6 +39,7 @@ export interface UseKeyboardShortcutsOptions {
enabled?: boolean; enabled?: boolean;
emails: Email[]; emails: Email[];
selectedEmailId?: string; selectedEmailId?: string;
selectionCount?: number;
handlers: KeyboardShortcutHandlers; handlers: KeyboardShortcutHandlers;
} }
@@ -58,6 +59,7 @@ export function useKeyboardShortcuts({
enabled = true, enabled = true,
emails, emails,
selectedEmailId, selectedEmailId,
selectionCount = 0,
handlers, handlers,
}: UseKeyboardShortcutsOptions) { }: UseKeyboardShortcutsOptions) {
const handlersRef = useRef(handlers); const handlersRef = useRef(handlers);
@@ -90,6 +92,8 @@ export function useKeyboardShortcuts({
// Shortcuts that should NOT work with modifiers // Shortcuts that should NOT work with modifiers
if (hasModifier) return; if (hasModifier) return;
const hasBatchTarget = !!selectedEmailId || selectionCount > 0;
switch (key) { switch (key) {
// Navigation // Navigation
case "j": case "j":
@@ -152,7 +156,7 @@ export function useKeyboardShortcuts({
break; break;
case "e": case "e":
if (selectedEmailId) { if (hasBatchTarget) {
event.preventDefault(); event.preventDefault();
h.onArchive?.(); h.onArchive?.();
} }
@@ -161,28 +165,28 @@ export function useKeyboardShortcuts({
case "#": case "#":
case "delete": case "delete":
case "backspace": case "backspace":
if (selectedEmailId && (key === "#" || key === "delete" || key === "backspace")) { if (hasBatchTarget) {
event.preventDefault(); event.preventDefault();
h.onDelete?.(); h.onDelete?.();
} }
break; break;
case "u": case "u":
if (selectedEmailId) { if (hasBatchTarget) {
event.preventDefault(); event.preventDefault();
h.onMarkAsUnread?.(); h.onMarkAsUnread?.();
} }
break; break;
case "i": case "i":
if (selectedEmailId && event.shiftKey) { if (hasBatchTarget && event.shiftKey) {
event.preventDefault(); event.preventDefault();
h.onMarkAsRead?.(); h.onMarkAsRead?.();
} }
break; break;
case "!": case "!":
if (selectedEmailId) { if (hasBatchTarget) {
event.preventDefault(); event.preventDefault();
h.onToggleSpam?.(); h.onToggleSpam?.();
} }
@@ -220,7 +224,7 @@ export function useKeyboardShortcuts({
break; break;
} }
}, },
[selectedEmailId] [selectedEmailId, selectionCount]
); );
useEffect(() => { useEffect(() => {