From d5835780a0ac2a6478f59393d56538901aeaa679 Mon Sep 17 00:00:00 2001 From: chrox Date: Sat, 9 Nov 2024 13:26:02 +0100 Subject: [PATCH] Add arrow down/up keybinding for scrolling page --- .../app/reader/components/FoliateViewer.tsx | 5 +- .../src/app/reader/hooks/useBookShortcuts.ts | 18 ++++- apps/readest-app/src/helpers/shortcuts.ts | 35 +++++---- apps/readest-app/src/hooks/useShortcuts.ts | 71 +++++-------------- apps/readest-app/src/store/readerStore.ts | 5 +- 5 files changed, 60 insertions(+), 74 deletions(-) diff --git a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx index b8afdfd5..86e03f04 100644 --- a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx +++ b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx @@ -12,6 +12,8 @@ export interface FoliateView extends HTMLElement { init: (options: { lastLocation: string }) => void; goTo: (href: string) => void; goToFraction: (fraction: number) => void; + prev: (distance: number) => void; + next: (distance: number) => void; goLeft: () => void; goRight: () => void; getCFI: (index: number, range: Range) => string; @@ -57,7 +59,8 @@ const FoliateViewer: React.FC<{ }; const handleKeydown = (event: KeyboardEvent) => { - if (event.key === 'Backspace') { + // prevent default navigation keys in iframes + if (['Backspace', 'ArrowDown', 'ArrowUp'].includes(event.key)) { event.preventDefault(); } window.postMessage( diff --git a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts index c346d46d..23c3b3a8 100644 --- a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts +++ b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts @@ -14,8 +14,12 @@ const useBookShortcuts = ({ openSplitView, getNextBookKey, }: UseBookShortcutsProps) => { - const { getView, setSideBarBookKey } = useReaderStore(); - const { toggleSideBar } = useReaderStore(); + const { getView, getConfig, toggleSideBar, setSideBarBookKey } = useReaderStore(); + const config = getConfig(sideBarBookKey); + const viewSettings = config?.viewSettings; + const fontSize = viewSettings?.defaultFontSize ?? 16; + const lineHeight = viewSettings?.lineHeight ?? 1.6; + const distance = fontSize * lineHeight * 3; const switchSideBar = () => { if (sideBarBookKey) setSideBarBookKey(getNextBookKey(sideBarBookKey)); @@ -29,6 +33,14 @@ const useBookShortcuts = ({ getView(sideBarBookKey)?.goRight(); }; + const goPrev = () => { + getView(sideBarBookKey)?.prev(distance); + }; + + const goNext = () => { + getView(sideBarBookKey)?.next(distance); + }; + const reloadPage = () => { window.location.reload(); }; @@ -41,6 +53,8 @@ const useBookShortcuts = ({ onReloadPage: reloadPage, onGoLeft: goLeft, onGoRight: goRight, + onGoPrev: goPrev, + onGoNext: goNext, }, [sideBarBookKey, bookKeys], ); diff --git a/apps/readest-app/src/helpers/shortcuts.ts b/apps/readest-app/src/helpers/shortcuts.ts index a5811355..a0ee9db7 100644 --- a/apps/readest-app/src/helpers/shortcuts.ts +++ b/apps/readest-app/src/helpers/shortcuts.ts @@ -1,25 +1,32 @@ export interface ShortcutConfig { - switchSidebar: string[]; - toggleSidebar: string[]; - openSplitView: string[]; - reloadPage: string[]; - goLeft: string[]; - goRight: string[]; + onSwitchSideBar: string[]; + onToggleSideBar: string[]; + onOpenSplitView: string[]; + onReloadPage: string[]; + onGoLeft: string[]; + onGoRight: string[]; + onGoNext: string[]; + onGoPrev: string[]; } const DEFAULT_SHORTCUTS: ShortcutConfig = { - switchSidebar: ['ctrl+Tab', 'opt+Tab', 'alt+Tab'], - toggleSidebar: ['t'], - openSplitView: ['shift+p'], - reloadPage: ['shift+r'], - goLeft: ['ArrowLeft', 'PageUp', 'h'], - goRight: ['ArrowRight', 'PageDown', 'l'], + onSwitchSideBar: ['ctrl+Tab', 'opt+Tab', 'alt+Tab'], + onToggleSideBar: ['t'], + onOpenSplitView: ['shift+p'], + onReloadPage: ['shift+r'], + onGoLeft: ['ArrowLeft', 'PageUp', 'h'], + onGoRight: ['ArrowRight', 'PageDown', 'l'], + onGoNext: ['ArrowDown', 'j'], + onGoPrev: ['ArrowUp', 'k'], }; // Load shortcuts from localStorage or fallback to defaults export const loadShortcuts = (): ShortcutConfig => { - const storedShortcuts = localStorage.getItem('customShortcuts'); - return storedShortcuts ? JSON.parse(storedShortcuts) : DEFAULT_SHORTCUTS; + const customShortcuts = JSON.parse(localStorage.getItem('customShortcuts') || '{}'); + return { + ...DEFAULT_SHORTCUTS, + ...customShortcuts, + }; }; // Save custom shortcuts to localStorage diff --git a/apps/readest-app/src/hooks/useShortcuts.ts b/apps/readest-app/src/hooks/useShortcuts.ts index 5f7f7758..1ac1c258 100644 --- a/apps/readest-app/src/hooks/useShortcuts.ts +++ b/apps/readest-app/src/hooks/useShortcuts.ts @@ -1,14 +1,9 @@ import { useEffect, useState } from 'react'; import { loadShortcuts, ShortcutConfig } from '../helpers/shortcuts'; -interface KeyActionHandlers { - onSwitchSideBar?: () => void; - onToggleSideBar?: () => void; - onOpenSplitView?: () => void; - onReloadPage?: () => void; - onGoLeft?: () => void; - onGoRight?: () => void; -} +export type KeyActionHandlers = { + [K in keyof ShortcutConfig]?: () => void; +}; const useShortcuts = (actions: KeyActionHandlers, dependencies: React.DependencyList = []) => { const [shortcuts, setShortcuts] = useState(loadShortcuts); @@ -60,53 +55,19 @@ const useShortcuts = (actions: KeyActionHandlers, dependencies: React.Dependency ) => { // FIXME: This is a temporary fix to disable Back button navigation if (key === 'backspace') return true; - if ( - shortcuts.switchSidebar.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onSwitchSideBar?.(); - return true; - } - if ( - shortcuts.toggleSidebar.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onToggleSideBar?.(); - return true; - } - if ( - shortcuts.openSplitView.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onOpenSplitView?.(); - return true; - } - if ( - shortcuts.reloadPage.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onReloadPage?.(); - return true; - } - if ( - shortcuts.goLeft.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onGoLeft?.(); - return true; - } - if ( - shortcuts.goRight.some((shortcut) => - isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), - ) - ) { - actions.onGoRight?.(); - return true; + for (const [actionName, actionHandler] of Object.entries(actions)) { + const shortcutKey = actionName as keyof ShortcutConfig; + const handler = actionHandler as (() => void) | undefined; + const shortcutList = shortcuts[shortcutKey as keyof ShortcutConfig]; + if ( + handler && + shortcutList?.some((shortcut) => + isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), + ) + ) { + handler(); + return true; + } } return false; }; diff --git a/apps/readest-app/src/store/readerStore.ts b/apps/readest-app/src/store/readerStore.ts index 359dc7ae..284194e1 100644 --- a/apps/readest-app/src/store/readerStore.ts +++ b/apps/readest-app/src/store/readerStore.ts @@ -67,7 +67,7 @@ interface ReaderStore { ) => void; getProgress: (key: string) => BookProgress | null; setConfig: (key: string, config: BookConfig) => void; - getConfig: (key: string) => BookConfig | null; + getConfig: (key: string | null) => BookConfig | null; setView: (key: string, view: FoliateView) => void; getView: (key: string | null) => FoliateView | null; getViewsById: (id: string) => FoliateView[]; @@ -135,7 +135,8 @@ export const useReaderStore = create((set, get) => ({ }); }, - getConfig: (key: string) => { + getConfig: (key: string | null) => { + if (!key) return null; const id = key.split('-')[0]!; return get().booksData[id]?.config || null; },