From cf34db53c8c0208bbb9a7f762db36ec42662e1f1 Mon Sep 17 00:00:00 2001 From: William Grant Date: Wed, 12 Jun 2024 17:23:16 +1000 Subject: [PATCH] feat: added new hot key hook since useKey doesnt ignore special keys replaced all alpha numeric shortcuts --- .../buttons/CopyToClipboardButton.tsx | 26 +--- ts/components/dialog/EnterPasswordModal.tsx | 15 +- .../dialog/edit-profile/EditProfileDialog.tsx | 133 ++++++++---------- ts/hooks/useHotkey.tsx | 66 +++++++++ 4 files changed, 132 insertions(+), 108 deletions(-) create mode 100644 ts/hooks/useHotkey.tsx diff --git a/ts/components/buttons/CopyToClipboardButton.tsx b/ts/components/buttons/CopyToClipboardButton.tsx index ee047e8cd..fdfa97045 100644 --- a/ts/components/buttons/CopyToClipboardButton.tsx +++ b/ts/components/buttons/CopyToClipboardButton.tsx @@ -1,7 +1,7 @@ import { isEmpty } from 'lodash'; import { useState } from 'react'; import useCopyToClipboard from 'react-use/lib/useCopyToClipboard'; -import useKey from 'react-use/lib/useKey'; +import { useHotkey } from '../../hooks/useHotkey'; import { ToastUtils } from '../../session/utils'; import { SessionButton, SessionButtonProps } from '../basic/SessionButton'; import { SessionIconButton } from '../icon'; @@ -30,17 +30,7 @@ export const CopyToClipboardButton = (props: CopyToClipboardButtonProps) => { } }; - useKey( - (event: KeyboardEvent) => { - return event.key === 'c'; - }, - () => { - if (!hotkey) { - return; - } - onClick(); - } - ); + useHotkey('c', onClick, !hotkey); return ( { } }; - useKey( - (event: KeyboardEvent) => { - return event.key === 'c'; - }, - () => { - if (!hotkey) { - return; - } - onClick(); - } - ); + useHotkey('c', onClick, !hotkey); return ( { } }); - useKey( - (event: KeyboardEvent) => event.key === 'Enter', - (event: KeyboardEvent) => { - if (event.key === 'Enter') { - if (event.target === passwordInputRef.current) { - confirmPassword(); - } - } + useHotkey('Enter', (event: KeyboardEvent) => { + if (event.target === passwordInputRef.current) { + confirmPassword(); } - ); + }); return ( void, - inputRef: RefObject, - updatedProfileName: string, - setProfileName: (name: string) => void, - setProfileNameError: (error: string | undefined) => void, - loading: boolean - ) => - () => { - if (loading) { - return; - } - switch (mode) { - case 'edit': - case 'qr': - if (inputRef.current !== null && document.activeElement === inputRef.current) { - return; - } - setMode('default'); - if (mode === 'edit') { - setProfileNameError(undefined); - setProfileName(updatedProfileName); - } - break; - case 'default': - default: - } - }; +const handleKeyCancel = ( + mode: ProfileDialogModes, + setMode: (mode: ProfileDialogModes) => void, + inputRef: RefObject, + updatedProfileName: string, + setProfileName: (name: string) => void, + setProfileNameError: (error: string | undefined) => void, + loading: boolean +) => { + if (loading) { + return; + } + switch (mode) { + case 'edit': + case 'qr': + if (inputRef.current !== null && document.activeElement === inputRef.current) { + return; + } + setMode('default'); + if (mode === 'edit') { + setProfileNameError(undefined); + setProfileName(updatedProfileName); + } + break; + case 'default': + default: + } +}; const handleKeyEscape = ( mode: ProfileDialogModes, @@ -249,50 +247,35 @@ export const EditProfileDialog = () => { ); }; - useKey( - (event: KeyboardEvent) => { - return ( - event.key === 'v' || - event.key === 'Enter' || - event.key === 'Backspace' || - event.key === 'Esc' || - event.key === 'Escape' - ); - }, - (event: KeyboardEvent) => { - switch (event.key) { - case 'v': - handleKeyQRMode(mode, setMode, loading); - break; - case 'Enter': - handleKeyEditMode(mode, setMode, onClickOK, loading); - break; - case 'Backspace': - handleKeyCancel( - mode, - setMode, - inputRef, - updatedProfileName, - setProfileName, - setProfileNameError, - loading - ); - break; - case 'Esc': - case 'Escape': - handleKeyEscape( - mode, - setMode, - updatedProfileName, - setProfileName, - setProfileNameError, - loading, - dispatch - ); - break; - default: - } - } + useHotkey('v', () => handleKeyQRMode(mode, setMode, loading), loading); + useHotkey('Enter', () => handleKeyEditMode(mode, setMode, onClickOK, loading), loading); + useHotkey( + 'Backspace', + () => + handleKeyCancel( + mode, + setMode, + inputRef, + updatedProfileName, + setProfileName, + setProfileNameError, + loading + ), + loading + ); + useHotkey( + 'Escape', + () => + handleKeyEscape( + mode, + setMode, + updatedProfileName, + setProfileName, + setProfileNameError, + loading, + dispatch + ), + loading ); return ( diff --git a/ts/hooks/useHotkey.tsx b/ts/hooks/useHotkey.tsx new file mode 100644 index 000000000..156435a6b --- /dev/null +++ b/ts/hooks/useHotkey.tsx @@ -0,0 +1,66 @@ +import useKey, { Handler, KeyPredicate, UseKeyOptions } from 'react-use/lib/useKey'; + +function specialKeyPressed(event: KeyboardEvent) { + const pressed = []; + + if (event.metaKey) { + pressed.push('super'); + } + if (event.ctrlKey) { + pressed.push('ctrl'); + } + if (event.altKey) { + pressed.push('alt'); + } + if (event.shiftKey) { + pressed.push('shift'); + } + + return pressed.join(' + '); +} + +export function useHotkey( + key: string, + onPress: (event: KeyboardEvent) => void | Promise, + disabled?: boolean +) { + const opts: UseKeyOptions = {}; + + const predicate: KeyPredicate = event => { + const lowerKey = key.toLowerCase(); + const eventKey = event.key.toLowerCase(); + + window.log.debug( + `WIP: [useHotkey] key: ${key} lowerKey: ${lowerKey} eventKey: ${eventKey} event.target: ${JSON.stringify(event)}` + ); + + switch (lowerKey) { + case 'esc': + case 'escape': + return eventKey === 'escape' || eventKey === 'esc'; + default: + return eventKey === lowerKey; + } + }; + + const handler: Handler = event => { + if (disabled) { + window.log.debug(`WIP: [useHotkey] '${key}' is disabled. Triggered by ${event.key}`); + return; + } + + const specialKeys = specialKeyPressed(event); + + if (specialKeys) { + window.log.debug( + `WIP: [useHotkey] '${key}' was ignored because it was pressed with ${specialKeys}. Triggered by ${event.key} + ${specialKeys}` + ); + return; + } + + window.log.debug(`WIP: [useHotkey] '${key}' onPress event. Triggered by ${event.key}`); + void onPress(event); + }; + + useKey(predicate, handler, opts); +}