Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions frontend/src/components/MailApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { updateFaviconBadge } from '../themes.js';
import { shortcutBus } from '../utils/shortcutBus.js';
import { setPending, pendingMarkReadMap, completedMarkReadMap } from '../utils/pendingReads.js';
import { buildKeyMap, buildModKeyMap, getEffectiveShortcuts, getGroupedActions, parseModKey, modLabel, SPECIAL_KEYS, SPECIAL_KEY_LABELS } from '../utils/defaultShortcuts.js';
import { sidebarTransition } from '../utils/sidebarTransition.js';
import Sidebar from './Sidebar.jsx';
import MessageList from './MessageList.jsx';
import MessagePane from './MessagePane.jsx';
Expand Down Expand Up @@ -61,7 +62,7 @@ export default function MailApp() {
const { t } = useTranslation();
const {
setAccounts, setUnreadCounts, showAdmin,
setShowAdmin, setAdminTab, composing, sidebarCollapsed, layout,
setShowAdmin, setAdminTab, composing, sidebarCollapsed, toggleSidebar, layout,
unreadCounts, selectedAccountId, openCompose, setSelectedAccount,
shortcuts, selectedMessageId, setSelectedMessage,
mobileSidebarOpen, setMobileSidebarOpen, addNotification,
Expand Down Expand Up @@ -643,6 +644,13 @@ export default function MailApp() {
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps

// Whole left-sidebar collapse toggle (cmd+\). Mobile keeps its drawer-specific controls.
useEffect(() => {
const onToggleLeftSidebar = () => { if (!isMobile) toggleSidebar(); };
shortcutBus.on('toggleLeftSidebar', onToggleLeftSidebar);
return () => shortcutBus.off('toggleLeftSidebar', onToggleLeftSidebar);
}, [isMobile, toggleSidebar]);

// Whole right-sidebar collapse toggle (cmd+/). Re-subscribed when applicability
// flips so the handler never toggles a sidebar that is not rendered.
useEffect(() => {
Expand Down Expand Up @@ -832,12 +840,12 @@ export default function MailApp() {
// Disabled while dragging (mirrors Sidebar's isSidebarResizing guard):
// otherwise every mousemove's CSS-var write would animate toward the new
// width instead of tracking the cursor.
transition: isRightSidebarResizing ? 'none' : 'width 0.2s ease',
transition: isRightSidebarResizing ? 'none' : sidebarTransition('width'),
}}>
<div style={{
width: 'var(--right-sidebar-width, 296px)', height: '100%',
transform: rightSidebarHidden ? 'translateX(100%)' : 'translateX(0)',
transition: 'transform 0.2s ease',
transition: sidebarTransition('transform'),
}}>
{rightSidebarContent}
</div>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
folderDropPosition,
resolveFolderOrderDrop,
} from '../utils/sidebar.js';
import { sidebarTransition } from '../utils/sidebarTransition.js';
import { useMobile } from '../hooks/useMobile.js';
import LogoMark from './LogoMark.jsx';
import ProfileModal from './ProfileModal.jsx';
Expand Down Expand Up @@ -794,7 +795,7 @@ export default function Sidebar() {
borderRight: '1px solid var(--border-subtle)',
display: 'flex',
flexDirection: 'column',
transition: isSidebarResizing ? 'none' : 'width 0.2s ease, min-width 0.2s ease',
transition: isSidebarResizing ? 'none' : sidebarTransition('width', 'min-width'),
overflow: 'hidden',
}}>
{/* Header */}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Als Delegiert markieren",
"description": "Die ausgewählte Nachricht als Delegiert einordnen"
},
"toggleLeftSidebar": {
"label": "Linke Seitenleiste umschalten",
"description": "Linke Seitenleiste aus- oder einblenden"
},
"toggleRightSidebar": {
"label": "Rechte Seitenleiste umschalten",
"description": "Rechte Seitenleiste aus- oder einblenden"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Mark Delegated",
"description": "Classify the selected message as Delegated"
},
"toggleLeftSidebar": {
"label": "Toggle left sidebar",
"description": "Collapse or expand the left sidebar"
},
"toggleRightSidebar": {
"label": "Toggle right sidebar",
"description": "Hide or show the right sidebar"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Marcar Delegado",
"description": "Clasificar el mensaje seleccionado como Delegado"
},
"toggleLeftSidebar": {
"label": "Alternar la barra lateral izquierda",
"description": "Contraer o expandir la barra lateral izquierda"
},
"toggleRightSidebar": {
"label": "Alternar la barra lateral derecha",
"description": "Ocultar o mostrar la barra lateral derecha"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Marquer Délégué",
"description": "Classer le message sélectionné comme Délégué"
},
"toggleLeftSidebar": {
"label": "Afficher ou masquer la barre latérale gauche",
"description": "Réduire ou développer la barre latérale gauche"
},
"toggleRightSidebar": {
"label": "Afficher ou masquer la barre latérale droite",
"description": "Masquer ou afficher la barre latérale droite"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Segna Delegato",
"description": "Classifica il messaggio selezionato come Delegato"
},
"toggleLeftSidebar": {
"label": "Mostra o nascondi la barra laterale sinistra",
"description": "Comprimi o espandi la barra laterale sinistra"
},
"toggleRightSidebar": {
"label": "Mostra o nascondi la barra laterale destra",
"description": "Nascondi o mostra la barra laterale destra"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "Отметить как «Делегировано»",
"description": "Отнести выбранное сообщение к «Делегировано»"
},
"toggleLeftSidebar": {
"label": "Переключить левую боковую панель",
"description": "Свернуть или развернуть левую боковую панель"
},
"toggleRightSidebar": {
"label": "Переключить правую боковую панель",
"description": "Скрыть или показать правую боковую панель"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/locales/zhCN.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@
"label": "标记为已委派",
"description": "将所选邮件归类为已委派"
},
"toggleLeftSidebar": {
"label": "切换左侧边栏",
"description": "折叠或展开左侧边栏"
},
"toggleRightSidebar": {
"label": "切换右侧边栏",
"description": "隐藏或显示右侧边栏"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/utils/defaultShortcuts.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const ACTION_DEFS = {
prevMessage: { groupKey: 'shortcuts.groups.navigation', labelKey: 'shortcuts.actions.prevMessage.label', descriptionKey: 'shortcuts.actions.prevMessage.description', defaultKey: 'k' },
openMessage: { groupKey: 'shortcuts.groups.navigation', labelKey: 'shortcuts.actions.openMessage.label', descriptionKey: 'shortcuts.actions.openMessage.description', defaultKey: 'o' },
goInbox: { groupKey: 'shortcuts.groups.navigation', labelKey: 'shortcuts.actions.goInbox.label', descriptionKey: 'shortcuts.actions.goInbox.description', defaultKey: 'gi' },
toggleLeftSidebar: { groupKey: 'shortcuts.groups.navigation', labelKey: 'shortcuts.actions.toggleLeftSidebar.label', descriptionKey: 'shortcuts.actions.toggleLeftSidebar.description', defaultKey: 'ctrl+\\' },
toggleRightSidebar: { groupKey: 'shortcuts.groups.navigation', labelKey: 'shortcuts.actions.toggleRightSidebar.label', descriptionKey: 'shortcuts.actions.toggleRightSidebar.description', defaultKey: 'ctrl+/' },

// ── Message actions ────────────────────────────────────────────────────────
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/utils/defaultShortcuts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,11 @@ describe('buildModKeyMap', () => {
assert.equal(keyMap['/'], 'focusSearch', 'bare / stays the search key (separate map)');
assert.equal(warn.mock.callCount(), 0);
});

it('binds toggleLeftSidebar to ctrl+backslash without colliding with defaults', (t) => {
const warn = t.mock.method(console, 'warn', () => {});
const modMap = buildModKeyMap();
assert.equal(modMap['\\'], 'toggleLeftSidebar');
assert.equal(warn.mock.callCount(), 0);
});
});
5 changes: 5 additions & 0 deletions frontend/src/utils/sidebarTransition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const SIDEBAR_TRANSITION_TIMING = '0.2s ease';

export function sidebarTransition(...properties) {
return properties.map(property => `${property} ${SIDEBAR_TRANSITION_TIMING}`).join(', ');
}
16 changes: 16 additions & 0 deletions frontend/src/utils/sidebarTransition.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { sidebarTransition } from './sidebarTransition.js';

describe('sidebarTransition', () => {
it('uses the shared GTD timing and easing for one animated property', () => {
assert.equal(sidebarTransition('width'), 'width 0.2s ease');
});

it('applies the shared animation to every supplied property', () => {
assert.equal(
sidebarTransition('width', 'min-width'),
'width 0.2s ease, min-width 0.2s ease',
);
});
});