Add hotkey Q for quoting the currently focused post (#35941)

This commit is contained in:
diondiondion 2025-08-28 14:33:23 +02:00 committed by GitHub
parent dccd29fe25
commit 229cbc6a24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 36 additions and 1 deletions

View File

@ -105,6 +105,7 @@ const hotkeyMatcherMap = {
reply: just('r'), reply: just('r'),
favourite: just('f'), favourite: just('f'),
boost: just('b'), boost: just('b'),
quote: just('q'),
mention: just('m'), mention: just('m'),
open: any('enter', 'o'), open: any('enter', 'o'),
openProfile: just('p'), openProfile: just('p'),

View File

@ -96,6 +96,7 @@ class Status extends ImmutablePureComponent {
onReply: PropTypes.func, onReply: PropTypes.func,
onFavourite: PropTypes.func, onFavourite: PropTypes.func,
onReblog: PropTypes.func, onReblog: PropTypes.func,
onQuote: PropTypes.func,
onDelete: PropTypes.func, onDelete: PropTypes.func,
onDirect: PropTypes.func, onDirect: PropTypes.func,
onMention: PropTypes.func, onMention: PropTypes.func,
@ -276,6 +277,10 @@ class Status extends ImmutablePureComponent {
this.props.onReblog(this._properStatus(), e); this.props.onReblog(this._properStatus(), e);
}; };
handleHotkeyQuote = () => {
this.props.onQuote(this._properStatus());
};
handleHotkeyMention = e => { handleHotkeyMention = e => {
e.preventDefault(); e.preventDefault();
this.props.onMention(this._properStatus().get('account')); this.props.onMention(this._properStatus().get('account'));
@ -386,6 +391,7 @@ class Status extends ImmutablePureComponent {
reply: this.handleHotkeyReply, reply: this.handleHotkeyReply,
favourite: this.handleHotkeyFavourite, favourite: this.handleHotkeyFavourite,
boost: this.handleHotkeyBoost, boost: this.handleHotkeyBoost,
quote: this.handleHotkeyQuote,
mention: this.handleHotkeyMention, mention: this.handleHotkeyMention,
open: this.handleHotkeyOpen, open: this.handleHotkeyOpen,
openProfile: this.handleHotkeyOpenProfile, openProfile: this.handleHotkeyOpenProfile,

View File

@ -59,6 +59,10 @@ const messages = defineMessages({
defaultMessage: 'Private posts cannot be quoted', defaultMessage: 'Private posts cannot be quoted',
}, },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
reblog_or_quote: {
id: 'status.reblog_or_quote',
defaultMessage: 'Boost or quote',
},
reblog_cancel: { reblog_cancel: {
id: 'status.cancel_reblog_private', id: 'status.cancel_reblog_private',
defaultMessage: 'Unboost', defaultMessage: 'Unboost',
@ -176,7 +180,7 @@ export const StatusReblogButton: FC<ReblogButtonProps> = ({
> >
<IconButton <IconButton
title={intl.formatMessage( title={intl.formatMessage(
!disabled ? messages.reblog : messages.all_disabled, !disabled ? messages.reblog_or_quote : messages.all_disabled,
)} )}
icon='retweet' icon='retweet'
iconComponent={iconComponent} iconComponent={iconComponent}

View File

@ -12,6 +12,7 @@ import {
mentionCompose, mentionCompose,
directCompose, directCompose,
} from '../actions/compose'; } from '../actions/compose';
import { quoteComposeById } from '../actions/compose_typed';
import { import {
initDomainBlockModal, initDomainBlockModal,
unblockDomain, unblockDomain,
@ -46,6 +47,8 @@ import Status from '../components/status';
import { deleteModal } from '../initial_state'; import { deleteModal } from '../initial_state';
import { makeGetStatus, makeGetPictureInPicture } from '../selectors'; import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
import { isFeatureEnabled } from 'mastodon/utils/environment';
const makeMapStateToProps = () => { const makeMapStateToProps = () => {
const getStatus = makeGetStatus(); const getStatus = makeGetStatus();
const getPictureInPicture = makeGetPictureInPicture(); const getPictureInPicture = makeGetPictureInPicture();
@ -76,6 +79,12 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
onReblog (status, e) { onReblog (status, e) {
dispatch(toggleReblog(status.get('id'), e.shiftKey)); dispatch(toggleReblog(status.get('id'), e.shiftKey));
}, },
onQuote (status) {
if (isFeatureEnabled('outgoing_quotes')) {
dispatch(quoteComposeById(status.get('id')));
}
},
onFavourite (status) { onFavourite (status) {
dispatch(toggleFavourite(status.get('id'))); dispatch(toggleFavourite(status.get('id')));

View File

@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import InfoIcon from '@/material-icons/400-24px/info.svg?react'; import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { isFeatureEnabled } from 'mastodon/utils/environment';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' }, heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
@ -62,6 +63,12 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><kbd>b</kbd></td> <td><kbd>b</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.boost' defaultMessage='to boost' /></td> <td><FormattedMessage id='keyboard_shortcuts.boost' defaultMessage='to boost' /></td>
</tr> </tr>
{isFeatureEnabled('outgoing_quotes') && (
<tr>
<td><kbd>q</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.quote' defaultMessage='Quote post' /></td>
</tr>
)}
<tr> <tr>
<td><kbd>enter</kbd>, <kbd>o</kbd></td> <td><kbd>enter</kbd>, <kbd>o</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td> <td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td>

View File

@ -69,6 +69,7 @@ import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from
import ActionBar from './components/action_bar'; import ActionBar from './components/action_bar';
import { DetailedStatus } from './components/detailed_status'; import { DetailedStatus } from './components/detailed_status';
import { RefreshController } from './components/refresh_controller'; import { RefreshController } from './components/refresh_controller';
import { quoteComposeById } from '@/mastodon/actions/compose_typed';
const messages = defineMessages({ const messages = defineMessages({
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' }, revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
@ -409,6 +410,10 @@ class Status extends ImmutablePureComponent {
this.handleReblogClick(this.props.status); this.handleReblogClick(this.props.status);
}; };
handleHotkeyQuote = () => {
this.props.dispatch(quoteComposeById(this.props.status.get('id')));
};
handleHotkeyMention = e => { handleHotkeyMention = e => {
e.preventDefault(); e.preventDefault();
this.handleMentionClick(this.props.status.get('account')); this.handleMentionClick(this.props.status.get('account'));
@ -546,6 +551,7 @@ class Status extends ImmutablePureComponent {
reply: this.handleHotkeyReply, reply: this.handleHotkeyReply,
favourite: this.handleHotkeyFavourite, favourite: this.handleHotkeyFavourite,
boost: this.handleHotkeyBoost, boost: this.handleHotkeyBoost,
quote: this.handleHotkeyQuote,
mention: this.handleHotkeyMention, mention: this.handleHotkeyMention,
openProfile: this.handleHotkeyOpenProfile, openProfile: this.handleHotkeyOpenProfile,
toggleHidden: this.handleHotkeyToggleHidden, toggleHidden: this.handleHotkeyToggleHidden,

View File

@ -492,6 +492,7 @@
"keyboard_shortcuts.open_media": "Open media", "keyboard_shortcuts.open_media": "Open media",
"keyboard_shortcuts.pinned": "Open pinned posts list", "keyboard_shortcuts.pinned": "Open pinned posts list",
"keyboard_shortcuts.profile": "Open author's profile", "keyboard_shortcuts.profile": "Open author's profile",
"keyboard_shortcuts.quote": "Quote post",
"keyboard_shortcuts.reply": "Reply to post", "keyboard_shortcuts.reply": "Reply to post",
"keyboard_shortcuts.requests": "Open follow requests list", "keyboard_shortcuts.requests": "Open follow requests list",
"keyboard_shortcuts.search": "Focus search bar", "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.quotes.empty": "No one has quoted this post yet. When someone does, it will show up here.",
"status.read_more": "Read more", "status.read_more": "Read more",
"status.reblog": "Boost", "status.reblog": "Boost",
"status.reblog_or_quote": "Boost or quote",
"status.reblog_private": "Boost with original visibility", "status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted", "status.reblogged_by": "{name} boosted",
"status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs": "{count, plural, one {boost} other {boosts}}",