diff --git a/src/components/keyboard-shortcuts-help.jsx b/src/components/keyboard-shortcuts-help.jsx index 0bf76daa..a3739ce8 100644 --- a/src/components/keyboard-shortcuts-help.jsx +++ b/src/components/keyboard-shortcuts-help.jsx @@ -129,6 +129,26 @@ export default function KeyboardShortcutsHelp() { action: 'Search', keys: /, }, + { + action: 'Reply', + keys: r, + }, + { + action: 'Favourite', + keys: f, + }, + { + action: 'Boost', + keys: ( + <> + Shift + b + + ), + }, + { + action: 'Bookmark', + keys: d, + }, ].map(({ action, keys }) => ( {action} diff --git a/src/components/status.jsx b/src/components/status.jsx index 5f85f235..955b65e0 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -19,6 +19,7 @@ import { useRef, useState, } from 'preact/hooks'; +import { useHotkeys } from 'react-hotkeys-hook'; import { InView } from 'react-intersection-observer'; import { useLongPress } from 'use-long-press'; import useResizeObserver from 'use-resize-observer'; @@ -621,8 +622,9 @@ function Status({ onClick={() => { try { favouriteStatus(); - if (!isSizeLarge) + if (!isSizeLarge) { showToast(favourited ? 'Unfavourited' : 'Favourited'); + } } catch (e) {} }} > @@ -644,8 +646,9 @@ function Status({ onClick={() => { try { bookmarkStatus(); - if (!isSizeLarge) + if (!isSizeLarge) { showToast(bookmarked ? 'Unbookmarked' : 'Bookmarked'); + } } catch (e) {} }} > @@ -832,9 +835,72 @@ function Status({ const showContextMenu = size !== 'l' && !previewMode && !_deleted && !quoted; + const hotkeysEnabled = !readOnly && !previewMode; + const rRef = useHotkeys('r', replyStatus, { + enabled: hotkeysEnabled, + }); + const fRef = useHotkeys( + 'f', + () => { + try { + favouriteStatus(); + if (!isSizeLarge) { + showToast(favourited ? 'Unfavourited' : 'Favourited'); + } + } catch (e) {} + }, + { + enabled: hotkeysEnabled, + }, + ); + const dRef = useHotkeys( + 'd', + () => { + try { + bookmarkStatus(); + if (!isSizeLarge) { + showToast(bookmarked ? 'Unbookmarked' : 'Bookmarked'); + } + } catch (e) {} + }, + { + enabled: hotkeysEnabled, + }, + ); + const bRef = useHotkeys( + 'shift+b', + () => { + (async () => { + try { + const done = await confirmBoostStatus(); + if (!isSizeLarge && done) { + showToast(reblogged ? 'Unboosted' : 'Boosted'); + } + } catch (e) {} + })(); + }, + { + enabled: hotkeysEnabled && canBoost, + }, + ); + return (
{ + statusRef.current = node; + // Use parent node if it's in focus + // Use case: + // When navigating (j/k), the is focused instead of + // Hotkey binding doesn't bubble up thus this hack + const nodeRef = + node?.closest?.( + '.timeline-item, .timeline-item-alt, .status-link, .status-focus', + ) || node; + rRef.current = nodeRef; + fRef.current = nodeRef; + dRef.current = nodeRef; + bRef.current = nodeRef; + }} tabindex="-1" class={`status ${ !withinContext && inReplyToId && inReplyToAccount