From 229cbc6a2495fa01780b75f4b2a80dc099fb5d5c Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 28 Aug 2025 14:33:23 +0200 Subject: [PATCH] Add hotkey Q for quoting the currently focused post (#35941) --- app/javascript/mastodon/components/hotkeys/index.tsx | 1 + app/javascript/mastodon/components/status.jsx | 6 ++++++ .../mastodon/components/status/reblog_button.tsx | 6 +++++- app/javascript/mastodon/containers/status_container.jsx | 9 +++++++++ .../mastodon/features/keyboard_shortcuts/index.jsx | 7 +++++++ app/javascript/mastodon/features/status/index.jsx | 6 ++++++ app/javascript/mastodon/locales/en.json | 2 ++ 7 files changed, 36 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/hotkeys/index.tsx b/app/javascript/mastodon/components/hotkeys/index.tsx index 33d11dab922..b1484ec3acb 100644 --- a/app/javascript/mastodon/components/hotkeys/index.tsx +++ b/app/javascript/mastodon/components/hotkeys/index.tsx @@ -105,6 +105,7 @@ const hotkeyMatcherMap = { reply: just('r'), favourite: just('f'), boost: just('b'), + quote: just('q'), mention: just('m'), open: any('enter', 'o'), openProfile: just('p'), diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 98b9ce5a823..bd1d4316223 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -96,6 +96,7 @@ class Status extends ImmutablePureComponent { onReply: PropTypes.func, onFavourite: PropTypes.func, onReblog: PropTypes.func, + onQuote: PropTypes.func, onDelete: PropTypes.func, onDirect: PropTypes.func, onMention: PropTypes.func, @@ -276,6 +277,10 @@ class Status extends ImmutablePureComponent { this.props.onReblog(this._properStatus(), e); }; + handleHotkeyQuote = () => { + this.props.onQuote(this._properStatus()); + }; + handleHotkeyMention = e => { e.preventDefault(); this.props.onMention(this._properStatus().get('account')); @@ -386,6 +391,7 @@ class Status extends ImmutablePureComponent { reply: this.handleHotkeyReply, favourite: this.handleHotkeyFavourite, boost: this.handleHotkeyBoost, + quote: this.handleHotkeyQuote, mention: this.handleHotkeyMention, open: this.handleHotkeyOpen, openProfile: this.handleHotkeyOpenProfile, diff --git a/app/javascript/mastodon/components/status/reblog_button.tsx b/app/javascript/mastodon/components/status/reblog_button.tsx index 23da29961ec..864b0120e8e 100644 --- a/app/javascript/mastodon/components/status/reblog_button.tsx +++ b/app/javascript/mastodon/components/status/reblog_button.tsx @@ -59,6 +59,10 @@ const messages = defineMessages({ defaultMessage: 'Private posts cannot be quoted', }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, + reblog_or_quote: { + id: 'status.reblog_or_quote', + defaultMessage: 'Boost or quote', + }, reblog_cancel: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost', @@ -176,7 +180,7 @@ export const StatusReblogButton: FC = ({ > { const getStatus = makeGetStatus(); const getPictureInPicture = makeGetPictureInPicture(); @@ -76,6 +79,12 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({ onReblog (status, e) { dispatch(toggleReblog(status.get('id'), e.shiftKey)); }, + + onQuote (status) { + if (isFeatureEnabled('outgoing_quotes')) { + dispatch(quoteComposeById(status.get('id'))); + } + }, onFavourite (status) { dispatch(toggleFavourite(status.get('id'))); diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx index e0a1dcc8822..a1f14f14f6d 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx @@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import InfoIcon from '@/material-icons/400-24px/info.svg?react'; import Column from 'mastodon/components/column'; import ColumnHeader from 'mastodon/components/column_header'; +import { isFeatureEnabled } from 'mastodon/utils/environment'; const messages = defineMessages({ heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' }, @@ -62,6 +63,12 @@ class KeyboardShortcuts extends ImmutablePureComponent { b + {isFeatureEnabled('outgoing_quotes') && ( + + q + + + )} enter, o diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx index d61b6fb5c2d..3c197150e5e 100644 --- a/app/javascript/mastodon/features/status/index.jsx +++ b/app/javascript/mastodon/features/status/index.jsx @@ -69,6 +69,7 @@ import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from import ActionBar from './components/action_bar'; import { DetailedStatus } from './components/detailed_status'; import { RefreshController } from './components/refresh_controller'; +import { quoteComposeById } from '@/mastodon/actions/compose_typed'; const messages = defineMessages({ revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' }, @@ -409,6 +410,10 @@ class Status extends ImmutablePureComponent { this.handleReblogClick(this.props.status); }; + handleHotkeyQuote = () => { + this.props.dispatch(quoteComposeById(this.props.status.get('id'))); + }; + handleHotkeyMention = e => { e.preventDefault(); this.handleMentionClick(this.props.status.get('account')); @@ -546,6 +551,7 @@ class Status extends ImmutablePureComponent { reply: this.handleHotkeyReply, favourite: this.handleHotkeyFavourite, boost: this.handleHotkeyBoost, + quote: this.handleHotkeyQuote, mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, toggleHidden: this.handleHotkeyToggleHidden, diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 9cf14b63b80..77c45fecbe7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -492,6 +492,7 @@ "keyboard_shortcuts.open_media": "Open media", "keyboard_shortcuts.pinned": "Open pinned posts list", "keyboard_shortcuts.profile": "Open author's profile", + "keyboard_shortcuts.quote": "Quote post", "keyboard_shortcuts.reply": "Reply to post", "keyboard_shortcuts.requests": "Open follow requests list", "keyboard_shortcuts.search": "Focus search bar", @@ -907,6 +908,7 @@ "status.quotes.empty": "No one has quoted this post yet. When someone does, it will show up here.", "status.read_more": "Read more", "status.reblog": "Boost", + "status.reblog_or_quote": "Boost or quote", "status.reblog_private": "Boost with original visibility", "status.reblogged_by": "{name} boosted", "status.reblogs": "{count, plural, one {boost} other {boosts}}",