diff --git a/src/app.jsx b/src/app.jsx index 54dde201..b0b25df7 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -23,6 +23,7 @@ import Compose from './components/compose'; import ComposeButton from './components/compose-button'; import Drafts from './components/drafts'; import { ICONS } from './components/icon'; +import KeyboardShortcutsHelp from './components/keyboard-shortcuts-help'; import Loader from './components/loader'; import MediaModal from './components/media-modal'; import Modal from './components/modal'; @@ -192,7 +193,8 @@ function App() { snapStates.showAccount || snapStates.showDrafts || snapStates.showMediaModal || - snapStates.showShortcutsSettings; + snapStates.showShortcutsSettings || + snapStates.showKeyboardShortcutsHelp; useEffect(() => { if (!showModal) focusDeck(); }, [showModal]); @@ -433,6 +435,7 @@ function App() { + ); } diff --git a/src/components/icon.jsx b/src/components/icon.jsx index 34ed5e06..f26091cd 100644 --- a/src/components/icon.jsx +++ b/src/components/icon.jsx @@ -97,6 +97,7 @@ export const ICONS = { clipboard: () => import('@iconify-icons/mingcute/clipboard-line'), 'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'), 'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'), + keyboard: () => import('@iconify-icons/mingcute/keyboard-line'), }; function Icon({ diff --git a/src/components/keyboard-shortcuts-help.css b/src/components/keyboard-shortcuts-help.css new file mode 100644 index 00000000..1dbe69ec --- /dev/null +++ b/src/components/keyboard-shortcuts-help.css @@ -0,0 +1,22 @@ +#keyboard-shortcuts-help-container { + table { + th { + font-weight: normal; + text-align: start; + padding: 0.25em 0; + line-height: 1; + } + td { + padding: 0.25em 1em; + } + } + + kbd { + border-radius: 4px; + display: inline-block; + padding: 0.3em; + line-height: 1; + border: 1px solid var(--outline-color); + background-color: var(--bg-faded-color); + } +} diff --git a/src/components/keyboard-shortcuts-help.jsx b/src/components/keyboard-shortcuts-help.jsx new file mode 100644 index 00000000..0d876d9e --- /dev/null +++ b/src/components/keyboard-shortcuts-help.jsx @@ -0,0 +1,136 @@ +import './keyboard-shortcuts-help.css'; + +import { useHotkeys } from 'react-hotkeys-hook'; +import { useSnapshot } from 'valtio'; + +import states from '../utils/states'; + +import Icon from './icon'; +import Modal from './modal'; + +export default function KeyboardShortcutsHelp() { + const snapStates = useSnapshot(states); + + function onClose() { + states.showKeyboardShortcutsHelp = false; + } + + useHotkeys( + '?, shift+?', + (e) => { + console.log('help'); + states.showKeyboardShortcutsHelp = true; + }, + { + ignoreEventWhen: (e) => { + const hasModal = !!document.querySelector('#modal-container > *'); + return hasModal; + }, + }, + ); + + const escRef = useHotkeys('esc', onClose, []); + + return ( + !!snapStates.showKeyboardShortcutsHelp && ( + { + if (e.target === e.currentTarget) { + onClose(); + } + }} + > +
+ +
+

Keyboard shortcuts

+
+
+ + {[ + { + action: 'Keyboard shortcuts help', + keys: ?, + }, + { + action: 'Next post', + keys: j, + }, + { + action: 'Previous post', + keys: k, + }, + { + action: 'Skip carousel to next post', + keys: ( + <> + Shift + j + + ), + }, + { + action: 'Skip carousel to previous post', + keys: ( + <> + Shift + k + + ), + }, + { + action: 'Search', + keys: /, + }, + { + action: 'Compose new post', + keys: c, + }, + { + action: 'Send post', + keys: ( + <> + Ctrl + Enter or +{' '} + Enter + + ), + }, + { + action: 'Open post details', + keys: ( + <> + Enter or o + + ), + }, + { + action: 'Toggle expanded/collapsed thread', + keys: x, + }, + { + action: 'Close post or dialogs', + keys: ( + <> + Esc or Backspace + + ), + }, + ].map(({ action, keys }) => ( + + + + + ))} +
{action}{keys}
+
+
+
+ ) + ); +} diff --git a/src/components/nav-menu.jsx b/src/components/nav-menu.jsx index 45e978ba..e92e96c1 100644 --- a/src/components/nav-menu.jsx +++ b/src/components/nav-menu.jsx @@ -204,6 +204,14 @@ function NavMenu(props) { > Accounts… + { + states.showKeyboardShortcutsHelp = true; + }} + > + {' '} + Keyboard shortcuts + { states.showShortcutsSettings = true; diff --git a/src/index.css b/src/index.css index 751ec5f2..d91bf8eb 100644 --- a/src/index.css +++ b/src/index.css @@ -315,7 +315,8 @@ pre { tab-size: 2; } pre code, -code { +code, +kbd { font-size: 90%; font-family: var(--monospace-font); } diff --git a/src/utils/states.js b/src/utils/states.js index bcb56d9e..ef155404 100644 --- a/src/utils/states.js +++ b/src/utils/states.js @@ -38,6 +38,7 @@ const states = proxy({ showDrafts: false, showMediaModal: false, showShortcutsSettings: false, + showKeyboardShortcutsHelp: false, // Shortcuts shortcuts: store.account.get('shortcuts') ?? [], // Settings @@ -137,6 +138,7 @@ export function hideAllModals() { states.showDrafts = false; states.showMediaModal = false; states.showShortcutsSettings = false; + states.showKeyboardShortcutsHelp = false; } export function statusKey(id, instance) {