fix: let c compose from any Pro tab again except Calendar, whose c is New event

This commit is contained in:
Linus Rath
2026-11-03 15:55:06 +01:00
parent 47a7381c21
commit f51d6ac9ac
3 changed files with 51 additions and 7 deletions
@@ -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();
+11 -3
View File
@@ -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,
+2 -3
View File
@@ -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,