feat: remove old hotkeys and add new View hotkeys (#1918)

# Add Numeric Hotkeys for Category Selection in Mail List

## Description

This PR adds keyboard shortcuts for quickly toggling mail categories in the inbox view:
- Numbers 1-9 toggle the corresponding categories (based on their index)
- Number 0 clears all selected labels

The implementation uses React Hotkeys Hook to register these shortcuts within the mail-list scope, making View switching much faster for keyboard-oriented users.

## Type of Change

- [x]  New feature (non-breaking change which adds functionality)
- [x] 🎨 UI/UX improvement

## Areas Affected

- [x] User Interface/Experience

## Testing Done

- [x] Manual testing performed

## Checklist

- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added keyboard shortcuts (number keys 1–9) to quickly toggle category label filters in the mail category dropdown.
  * Added a keyboard shortcut (0 key) to clear all active category label filters.

* **Refactor**
  * Disabled previous keyboard shortcuts for switching mail list categories to avoid conflicts and streamline shortcut behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ahmet Kilinc
2025-08-05 19:58:27 +01:00
committed by GitHub
parent cfdcb03502
commit 428e75ff93
2 changed files with 85 additions and 46 deletions

View File

@@ -9,6 +9,7 @@ import { Bell, Lightning, Mail, ScanEye, Tag, User, X, Search } from '../icons/i
import { useCategorySettings, useDefaultCategoryId } from '@/hooks/use-categories';
import { ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
import { useCommandPalette } from '../context/command-palette-context';
import { useHotkeys, useHotkeysContext } from 'react-hotkeys-hook';
import { ThreadDisplay } from '@/components/mail/thread-display';
import { useActiveConnection } from '@/hooks/use-connections';
import { Check, ChevronDown, RefreshCcw } from 'lucide-react';
@@ -17,7 +18,6 @@ import useSearchLabels from '@/hooks/use-labels-search';
import * as CustomIcons from '@/components/icons/icons';
import { isMac } from '@/lib/hotkeys/use-hotkey-utils';
import { MailList } from '@/components/mail/mail-list';
import { useHotkeysContext } from 'react-hotkeys-hook';
import { useNavigate, useParams } from 'react-router';
import { useMail } from '@/components/mail/use-mail';
import { SidebarToggle } from '../ui/sidebar-toggle';
@@ -678,6 +678,42 @@ function CategoryDropdown({ isMultiSelectMode }: CategoryDropdownProps) {
const folder = params?.folder ?? 'inbox';
const [isOpen, setIsOpen] = useState(false);
categorySettings.forEach((category, index) => {
if (index < 9) {
const keyNumber = (index + 1).toString();
useHotkeys(
keyNumber,
() => {
const isCurrentlyActive = labels.includes(category.searchValue);
if (isCurrentlyActive) {
setLabels(labels.filter((label) => label !== category.searchValue));
} else {
setLabels([...labels, category.searchValue]);
}
},
{
scopes: ['mail-list'],
preventDefault: true,
enableOnFormTags: false,
},
[category.searchValue, labels, setLabels], // Dependencies
);
}
});
useHotkeys(
'0',
() => {
setLabels([]);
},
{
scopes: ['mail-list'],
preventDefault: true,
enableOnFormTags: false,
},
);
if (folder !== 'inbox' || isMultiSelectMode) return null;
const handleLabelChange = (searchValue: string) => {

View File

@@ -1,13 +1,16 @@
import { useOptimisticActions } from '@/hooks/use-optimistic-actions';
import { enhancedKeyboardShortcuts } from '@/config/shortcuts';
import { useSearchValue } from '@/hooks/use-search-value';
import { useLocation, useParams } from 'react-router';
// import { useSearchValue } from '@/hooks/use-search-value';
import {
// useLocation,
useParams,
} from 'react-router';
import { useMail } from '@/components/mail/use-mail';
import { useCallback, useMemo, useRef } from 'react';
import { Categories } from '@/components/mail/mail';
// import { Categories } from '@/components/mail/mail';
import { useShortcuts } from './use-hotkey-utils';
import { useThreads } from '@/hooks/use-threads';
import { cleanSearchValue } from '@/lib/utils';
// import { cleanSearchValue } from '@/lib/utils';
import { m } from '@/paraglide/messages';
import { toast } from 'sonner';
@@ -16,9 +19,9 @@ export function MailListHotkeys() {
const [mail, setMail] = useMail();
const [, items] = useThreads();
const hoveredEmailId = useRef<string | null>(null);
const categories = Categories();
const [searchValue, setSearchValue] = useSearchValue();
const pathname = useLocation().pathname;
// const categories = Categories();
// const [searchValue, setSearchValue] = useSearchValue();
// const pathname = useLocation().pathname;
const params = useParams<{ folder: string }>();
const folder = params?.folder ?? 'inbox';
const shouldUseHover = mail.bulkSelected.length === 0;
@@ -172,38 +175,38 @@ export function MailListHotkeys() {
}));
}, [shouldUseHover]);
const switchMailListCategory = useCallback(
(category: string | null) => {
if (pathname?.includes('/mail/inbox')) {
const cat = categories.find((cat) => cat.id === category);
if (!cat) {
// setCategory(null);
setSearchValue({
value: '',
highlight: searchValue.highlight,
folder: '',
});
return;
}
// setCategory(cat.id);
setSearchValue({
value: `${cat.searchValue} ${cleanSearchValue(searchValue.value).trim().length ? `AND ${cleanSearchValue(searchValue.value)}` : ''}`,
highlight: searchValue.highlight,
folder: '',
});
}
},
[categories, pathname, searchValue, setSearchValue],
);
// const switchMailListCategory = useCallback(
// (category: string | null) => {
// if (pathname?.includes('/mail/inbox')) {
// const cat = categories.find((cat) => cat.id === category);
// if (!cat) {
// // setCategory(null);
// setSearchValue({
// value: '',
// highlight: searchValue.highlight,
// folder: '',
// });
// return;
// }
// // setCategory(cat.id);
// setSearchValue({
// value: `${cat.searchValue} ${cleanSearchValue(searchValue.value).trim().length ? `AND ${cleanSearchValue(searchValue.value)}` : ''}`,
// highlight: searchValue.highlight,
// folder: '',
// });
// }
// },
// [categories, pathname, searchValue, setSearchValue],
// );
const switchCategoryByIndex = useCallback(
(idx: number) => {
const cat = categories[idx];
if (!cat) return;
switchMailListCategory(cat.id);
},
[categories, switchMailListCategory],
);
// const switchCategoryByIndex = useCallback(
// (idx: number) => {
// const cat = categories[idx];
// if (!cat) return;
// switchMailListCategory(cat.id);
// },
// [categories, switchMailListCategory],
// );
const handlers = useMemo(
() => ({
@@ -216,15 +219,15 @@ export function MailListHotkeys() {
bulkDelete,
bulkStar,
exitSelectionMode,
showImportant: () => switchCategoryByIndex(0),
showAllMail: () => switchCategoryByIndex(1),
showPersonal: () => switchCategoryByIndex(2),
showUpdates: () => switchCategoryByIndex(3),
showPromotions: () => switchCategoryByIndex(4),
showUnread: () => switchCategoryByIndex(5),
// showImportant: () => switchCategoryByIndex(0),
// showAllMail: () => switchCategoryByIndex(1),
// showPersonal: () => switchCategoryByIndex(2),
// showUpdates: () => switchCategoryByIndex(3),
// showPromotions: () => switchCategoryByIndex(4),
// showUnread: () => switchCategoryByIndex(5),
}),
[
switchCategoryByIndex,
// switchCategoryByIndex,
markAsRead,
markAsUnread,
markAsImportant,