From f51d6ac9acf827bb2f6d5a5fa5a5abac39915cd2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Tue, 3 Nov 2026 15:55:06 +0100 Subject: [PATCH] fix: let c compose from any Pro tab again except Calendar, whose c is New event --- .../__tests__/mail-app-pro-shortcuts.test.tsx | 39 ++++++++++++++++++- components/mail/mail-app.tsx | 14 +++++-- components/pro/pro-email-tab-body.tsx | 5 +-- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/components/mail/__tests__/mail-app-pro-shortcuts.test.tsx b/components/mail/__tests__/mail-app-pro-shortcuts.test.tsx index 4dc6a83c0..f449f45d0 100644 --- a/components/mail/__tests__/mail-app-pro-shortcuts.test.tsx +++ b/components/mail/__tests__/mail-app-pro-shortcuts.test.tsx @@ -10,6 +10,7 @@ import { useThemeStore } from '@/stores/theme-store'; import { DEFAULT_SEARCH_FILTERS } from '@/lib/jmap/search-utils'; import { EmbeddedContext } from '@/hooks/use-is-embedded'; import { ProTabFocusContext } from '@/hooks/use-pane-context'; +import { useProTabStore, type ProTabKind } from '@/stores/pro-tab-store'; // Under Pro the mail page stays mounted behind other tabs. Its shortcuts acted // from there: e on the Calendar tab archived the mail selected in the hidden @@ -102,14 +103,50 @@ function renderPro(focused: boolean) { ); } +function withTabInFront(kind: ProTabKind) { + useProTabStore.setState({ + tabs: [ + { id: 'home-mail', kind: 'mail', labelKey: 'mail', closeable: false, paneId: 'main' }, + { id: 'front', kind, labelKey: kind, closeable: true, paneId: 'main' }, + ], + activeTabId: 'front', activeSplitTabId: null, focusedPaneId: 'main', + } as never); +} + describe('mail shortcuts under the Pro shell', () => { - it('leaves only the help on while another tab is in front', () => { + it.each(['files', 'settings', 'contacts', 'folder', 'search', 'email', 'compose'] as ProTabKind[])( + 'leaves the help and a new message on while a %s tab is in front, and nothing that acts on mail', + (kind) => { + withTabInFront(kind); + renderPro(false); + const active = activeShortcuts(); + expect(active).toHaveLength(1); + expect(Object.keys(active[0].handlers).sort()).toEqual(['onCompose', 'onShowHelp']); + }, + ); + + it('leaves c to the Calendar tab, whose c is New event', () => { + withTabInFront('calendar'); renderPro(false); const active = activeShortcuts(); expect(active).toHaveLength(1); expect(Object.keys(active[0].handlers)).toEqual(['onShowHelp']); }); + it('goes by the pane in focus when the Calendar tab is in the other one', () => { + withTabInFront('calendar'); + useProTabStore.setState({ + tabs: [ + { id: 'home-mail', kind: 'mail', labelKey: 'mail', closeable: false, paneId: 'main' }, + { id: 'front', kind: 'calendar', labelKey: 'calendar', closeable: true, paneId: 'main' }, + { id: 'files', kind: 'files', labelKey: 'files', closeable: true, paneId: 'split' }, + ], + activeSplitTabId: 'files', focusedPaneId: 'split', + } as never); + renderPro(false); + expect(activeShortcuts()[0].handlers.onCompose).toBeTypeOf('function'); + }); + it('has the mail shortcuts on while the mail tab is in front', () => { renderPro(true); const active = activeShortcuts(); diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx index 5ae8a3bc6..f80b0c5ca 100644 --- a/components/mail/mail-app.tsx +++ b/components/mail/mail-app.tsx @@ -1244,9 +1244,14 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) { const singleKeyShortcuts = useSettingsStore((s) => s.singleKeyShortcuts); // Under Pro this page stays mounted behind other tabs. Its mail shortcuts // are only for when it is in front: e on the Calendar tab archived the mail - // selected here, out of sight. The help opens from any tab. (c stays each - // surface's own: New event on the Calendar tab.) + // selected here, out of sight. The help opens from any tab, and so does a new + // message, which touches nothing out of sight, except on the Calendar tab: its + // c is New event. const isFocusedProTab = useIsFocusedProTab(); + const calendarTabInFront = useProTabStore((state) => { + const id = state.focusedPaneId === 'split' ? state.activeSplitTabId : state.activeTabId; + return state.tabs.find((tab) => tab.id === id)?.kind === 'calendar'; + }); const mailShortcutsApply = !isEmbedded || isFocusedProTab; useKeyboardShortcuts({ enabled: isAuthenticated && !showComposer && mailShortcutsApply, @@ -1257,7 +1262,10 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) { mountainViewKeys: mvShell, handlers: keyboardHandlers, }); - const anyTabHandlers = useMemo(() => ({ onShowHelp: keyboardHandlers.onShowHelp }), [keyboardHandlers]); + const anyTabHandlers = useMemo(() => ({ + onShowHelp: keyboardHandlers.onShowHelp, + ...(calendarTabInFront ? {} : { onCompose: keyboardHandlers.onCompose }), + }), [keyboardHandlers, calendarTabInFront]); useKeyboardShortcuts({ enabled: isAuthenticated && !showComposer && !mailShortcutsApply, singleKeyShortcuts, diff --git a/components/pro/pro-email-tab-body.tsx b/components/pro/pro-email-tab-body.tsx index 1000e136e..ff4769e2d 100644 --- a/components/pro/pro-email-tab-body.tsx +++ b/components/pro/pro-email-tab-body.tsx @@ -427,12 +427,11 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId, }, [client, email, moveToMailbox, onClose]); // This view's message answers the same keys as a message in the mail tab, - // while its tab is the one in front. + // while its tab is the one in front. c comes from the mail page, as on any tab. const isFocusedTab = useIsFocusedProTab(); const singleKeyShortcuts = useSettingsStore((s) => s.singleKeyShortcuts); const shortcutEmails = useMemo(() => (email ? [email] : []), [email]); const shortcutHandlers = useMemo(() => ({ - onCompose: () => openComposeTab({ mode: 'compose', title: t('email_composer.new_message') }, {}), onReply: () => handleReply(), onReplyAll: handleReplyAll, onForward: handleForward, @@ -441,7 +440,7 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId, onDelete: () => void handleDelete(), onMarkAsUnread: email ? () => void handleMarkAsRead(email.id, false) : undefined, onMarkAsRead: email ? () => void handleMarkAsRead(email.id, true) : undefined, - }), [email, openComposeTab, t, handleReply, handleReplyAll, handleForward, handleToggleStar, archiveActionEnabled, handleArchive, handleDelete, handleMarkAsRead]); + }), [email, handleReply, handleReplyAll, handleForward, handleToggleStar, archiveActionEnabled, handleArchive, handleDelete, handleMarkAsRead]); useKeyboardShortcuts({ enabled: isFocusedTab && !!email, singleKeyShortcuts,