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 });
}, [currentUserId, openChat]);
useHotkeys([
['mod+shift+F', handleHotkeySearch],
['mod+shift+S', handleHotkeySavedMessages],
]);
useHotkeys({
'mod+shift+F': handleHotkeySearch,
'mod+shift+S': handleHotkeySavedMessages,
});
useEffect(() => {
clearTwoFaError();

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ const useCopySelectedMessages = (isActive: boolean, copySelectedMessages: NoneTo
copySelectedMessages();
}
useHotkeys([['meta+C', handleCopy]]);
useHotkeys({ 'meta+C': handleCopy });
};
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 { 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) {
if (event.target instanceof HTMLElement) {
return !['INPUT', 'TEXTAREA', 'SELECT'].includes(event.target.tagName);
}
return true;
}
export function useHotkeys(hotkeys: HotkeyItem[]) {
export function useHotkeys(hotkeys?: Record<string, (e: KeyboardEvent) => void>) {
useEffect(() => {
const keydownListener = (event: KeyboardEvent) => {
hotkeys.forEach(([hotkey, handler]) => {
if (getHotkeyMatcher(hotkey)(event) && shouldFireEvent(event)) {
handler(event);
if (!hotkeys) {
return undefined;
}
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 () => document.documentElement.removeEventListener('keydown', keydownListener);
return handlers.addCallback(handleKeyDown);
}, [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;

View File

@ -13,8 +13,6 @@ export type Hotkey = KeyboardModifiers & {
key?: string;
};
type HotkeyItem = [string, (event: React.KeyboardEvent<HTMLElement>) => void];
type CheckHotkeyMatch = (event: KeyboardEvent) => boolean;
export function parseHotkey(hotkey: string): Hotkey {
@ -77,14 +75,3 @@ function isExactHotkey(hotkey: Hotkey, event: KeyboardEvent): boolean {
export function getHotkeyMatcher(hotkey: string): CheckHotkeyMatch {
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);
}
});
};
}