diff --git a/app/javascript/flavours/glitch/components/hotkeys/index.tsx b/app/javascript/flavours/glitch/components/hotkeys/index.tsx index e923dc0d65..99e8bc7966 100644 --- a/app/javascript/flavours/glitch/components/hotkeys/index.tsx +++ b/app/javascript/flavours/glitch/components/hotkeys/index.tsx @@ -110,8 +110,8 @@ const hotkeyMatcherMap = { mention: just('m'), open: any('enter', 'o'), openProfile: just('p'), - moveDown: just('j'), - moveUp: just('k'), + moveDown: any('j', 'pagedown'), + moveUp: any('k', 'pageup'), moveToTop: just('0'), toggleHidden: just('x'), toggleSensitive: just('h'), @@ -148,9 +148,15 @@ const hotkeyMatcherMap = { type HotkeyName = keyof typeof hotkeyMatcherMap; -export type HandlerMap = Partial< - Record void> ->; +type HandlerFunction = + // When a handler returns a boolean, it should indicate whether the + // hotkey was handled (i.e. it resulted in an action). + // If `false` is returned, `preventDefault` and `stopPropagation` + // will not be called on the keyboard event, restoring the key's + // native behaviour. + ((event: KeyboardEvent) => boolean) | ((event: KeyboardEvent) => void); + +export type HandlerMap = Partial>; export function useHotkeys(handlers: HandlerMap) { const ref = useRef(null); @@ -185,7 +191,7 @@ export function useHotkeys(handlers: HandlerMap) { const matchCandidates: { // A candidate will be have an undefined handler if it's matched, // but handled in a parent component rather than this one. - handler: ((event: KeyboardEvent) => void) | undefined; + handler: HandlerFunction | undefined; priority: number; }[] = []; @@ -210,9 +216,11 @@ export function useHotkeys(handlers: HandlerMap) { const bestMatchingHandler = matchCandidates.at(0)?.handler; if (bestMatchingHandler) { - bestMatchingHandler(event); - event.stopPropagation(); - event.preventDefault(); + const wasHandled = bestMatchingHandler(event); + if (wasHandled !== false) { + event.stopPropagation(); + event.preventDefault(); + } } // Add last keypress to buffer diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx index aa656fc2b8..8f75671322 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx @@ -72,7 +72,7 @@ class KeyboardShortcuts extends ImmutablePureComponent { - enter, o + , o @@ -92,11 +92,11 @@ class KeyboardShortcuts extends ImmutablePureComponent { - k + k, - j + j, @@ -116,15 +116,15 @@ class KeyboardShortcuts extends ImmutablePureComponent { - alt+n + +n - alt+x + +x - backspace + @@ -136,7 +136,7 @@ class KeyboardShortcuts extends ImmutablePureComponent { - esc + diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index e973167880..f34be17736 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -554,7 +554,8 @@ class UI extends PureComponent { if (currentItemIndex === -1) { focusColumn(1); } else { - focusItemSibling(currentItemIndex, -1); + const wasHandled = focusItemSibling(currentItemIndex, -1); + return wasHandled; } }; @@ -563,7 +564,8 @@ class UI extends PureComponent { if (currentItemIndex === -1) { focusColumn(1); } else { - focusItemSibling(currentItemIndex, 1); + const wasHandled = focusItemSibling(currentItemIndex, 1); + return wasHandled; } }; diff --git a/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts b/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts index c632a0a51d..80e9cb9768 100644 --- a/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts +++ b/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts @@ -159,13 +159,12 @@ export function focusItemSibling(index: number, direction: 1 | -1) { ); if (!siblingItem) { - return; + return false; } // If sibling element is empty, we skip it if (siblingItem.matches(':empty')) { - focusItemSibling(index + direction, direction); - return; + return focusItemSibling(index + direction, direction); } // Check if the sibling is a post or a 'follow suggestions' widget @@ -184,5 +183,8 @@ export function focusItemSibling(index: number, direction: 1 | -1) { }); targetElement.focus(); + return true; + } else { + return false; } }