Refactoring and optimizations for useHotkeys

This commit is contained in:
Alexander Zinchuk 2022-05-06 17:56:07 +01:00
parent f91960b90c
commit 84ea8e480e
8 changed files with 47 additions and 54 deletions

View File

@ -282,10 +282,10 @@ const LeftColumn: FC<StateProps> = ({
openChat({ id: currentUserId }); openChat({ id: currentUserId });
}, [currentUserId, openChat]); }, [currentUserId, openChat]);
useHotkeys([ useHotkeys({
['mod+shift+F', handleHotkeySearch], 'mod+shift+F': handleHotkeySearch,
['mod+shift+S', handleHotkeySavedMessages], 'mod+shift+S': handleHotkeySavedMessages,
]); });
useEffect(() => { useEffect(() => {
clearTwoFaError(); clearTwoFaError();

View File

@ -163,7 +163,7 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
return () => { return () => {
document.removeEventListener('keydown', handleKeyDown, true); document.removeEventListener('keydown', handleKeyDown, true);
}; };
}); }, [currentUserId, folderTabs, openChat, setActiveChatFolder]);
const { const {
shouldRender: shouldRenderPlaceholder, transitionClassNames, shouldRender: shouldRenderPlaceholder, transitionClassNames,

View File

@ -19,7 +19,7 @@ import usePrevious from '../../../hooks/usePrevious';
import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
import { useFolderManagerForOrderedIds } from '../../../hooks/useFolderManager'; import { useFolderManagerForOrderedIds } from '../../../hooks/useFolderManager';
import { useChatAnimationType } from './hooks'; import { useChatAnimationType } from './hooks';
import { HotkeyItem, useHotkeys } from '../../../hooks/useHotkeys'; import { useHotkeys } from '../../../hooks/useHotkeys';
import InfiniteScroll from '../../ui/InfiniteScroll'; import InfiniteScroll from '../../ui/InfiniteScroll';
import Loading from '../../ui/Loading'; import Loading from '../../ui/Loading';
@ -76,19 +76,16 @@ const ChatList: FC<OwnProps> = ({
const [viewportIds, getMore] = useInfiniteScroll(undefined, orderedIds, undefined, CHAT_LIST_SLICE); const [viewportIds, getMore] = useInfiniteScroll(undefined, orderedIds, undefined, CHAT_LIST_SLICE);
// Support <Alt>+<Up/Down> to navigate between chats // Support <Alt>+<Up/Down> to navigate between chats
const hotkeys: HotkeyItem[] = []; useHotkeys(isActive && orderedIds?.length ? {
if (isActive && orderedIds?.length) { 'alt+ArrowUp': (e: KeyboardEvent) => {
hotkeys.push(['alt+ArrowUp', (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
openNextChat({ targetIndexDelta: -1, orderedIds }); openNextChat({ targetIndexDelta: -1, orderedIds });
}]); },
hotkeys.push(['alt+ArrowDown', (e: KeyboardEvent) => { 'alt+ArrowDown': (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
openNextChat({ targetIndexDelta: 1, orderedIds }); openNextChat({ targetIndexDelta: 1, orderedIds });
}]); },
} } : undefined);
useHotkeys(hotkeys);
// Support <Cmd>+<Digit> to navigate between chats // Support <Cmd>+<Digit> to navigate between chats
useEffect(() => { useEffect(() => {

View File

@ -151,9 +151,9 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
handleSearchClick(); handleSearchClick();
}, [canSearch, handleSearchClick]); }, [canSearch, handleSearchClick]);
useHotkeys([ useHotkeys({
['meta+F', handleHotkeySearchClick], 'meta+F': handleHotkeySearchClick,
]); });
const lang = useLang(); const lang = useLang();

View File

@ -10,7 +10,7 @@ const useCopySelectedMessages = (isActive: boolean, copySelectedMessages: NoneTo
copySelectedMessages(); copySelectedMessages();
} }
useHotkeys([['meta+C', handleCopy]]); useHotkeys({ 'meta+C': handleCopy });
}; };
export default useCopySelectedMessages; export default useCopySelectedMessages;

View File

@ -1,31 +1,40 @@
// Original source from Mantine
// https://github.com/mantinedev/mantine/blob/master/src/mantine-hooks/src/use-hotkeys/
import { useEffect } from '../lib/teact/teact'; import { useEffect } from '../lib/teact/teact';
import { getHotkeyHandler, getHotkeyMatcher } from '../util/parseHotkey'; import { getHotkeyMatcher } from '../util/parseHotkey';
import { createCallbackManager } from '../util/callbacks';
export { getHotkeyHandler }; const IGNORE_TAGS = new Set(['INPUT', 'TEXTAREA', 'SELECT']);
export type HotkeyItem = [string, (event: KeyboardEvent) => void]; const handlers = createCallbackManager();
document.documentElement.addEventListener('keydown', handlers.runCallbacks);
function shouldFireEvent(event: KeyboardEvent) { export function useHotkeys(hotkeys?: Record<string, (e: KeyboardEvent) => void>) {
if (event.target instanceof HTMLElement) {
return !['INPUT', 'TEXTAREA', 'SELECT'].includes(event.target.tagName);
}
return true;
}
export function useHotkeys(hotkeys: HotkeyItem[]) {
useEffect(() => { useEffect(() => {
const keydownListener = (event: KeyboardEvent) => { if (!hotkeys) {
hotkeys.forEach(([hotkey, handler]) => { return undefined;
if (getHotkeyMatcher(hotkey)(event) && shouldFireEvent(event)) { }
handler(event);
const entries = Object.entries(hotkeys);
function handleKeyDown(e: KeyboardEvent) {
if (!shouldFireEvent(e)) {
return;
}
entries.forEach(([hotkey, handler]) => {
if (getHotkeyMatcher(hotkey)(e)) {
handler(e);
} }
}); });
}; }
document.documentElement.addEventListener('keydown', keydownListener); return handlers.addCallback(handleKeyDown);
return () => document.documentElement.removeEventListener('keydown', keydownListener);
}, [hotkeys]); }, [hotkeys]);
} }
function shouldFireEvent(e: KeyboardEvent) {
if (e.target instanceof HTMLElement) {
return !IGNORE_TAGS.has(e.target.tagName);
}
return true;
}

View File

@ -11,7 +11,7 @@ const useNativeCopySelectedMessages = (copyMessagesByIds: ({ messageIds }: { mes
} }
} }
useHotkeys([['meta+C', handleCopy]]); useHotkeys({ 'meta+C': handleCopy });
}; };
export default useNativeCopySelectedMessages; export default useNativeCopySelectedMessages;

View File

@ -13,8 +13,6 @@ export type Hotkey = KeyboardModifiers & {
key?: string; key?: string;
}; };
type HotkeyItem = [string, (event: React.KeyboardEvent<HTMLElement>) => void];
type CheckHotkeyMatch = (event: KeyboardEvent) => boolean; type CheckHotkeyMatch = (event: KeyboardEvent) => boolean;
export function parseHotkey(hotkey: string): Hotkey { export function parseHotkey(hotkey: string): Hotkey {
@ -77,14 +75,3 @@ function isExactHotkey(hotkey: Hotkey, event: KeyboardEvent): boolean {
export function getHotkeyMatcher(hotkey: string): CheckHotkeyMatch { export function getHotkeyMatcher(hotkey: string): CheckHotkeyMatch {
return (event) => isExactHotkey(parseHotkey(hotkey), event); return (event) => isExactHotkey(parseHotkey(hotkey), event);
} }
export function getHotkeyHandler(hotkeys: HotkeyItem[]) {
return (event: React.KeyboardEvent<HTMLElement>) => {
hotkeys.forEach(([hotkey, handler]) => {
if (getHotkeyMatcher(hotkey)(event.nativeEvent)) {
event.preventDefault();
handler(event);
}
});
};
}