Don't autofocus the compose form (#16517)

When opening a page such as /web/timelines/home in a desktop browser, the
cursor was automatically placed in the textarea of the compose form.

When using the keyboard for navigation (using a browser plugin like vimium or
vim vixen, or just to hit 'space' to scroll down a page), you have remember to
leave the field before using that.

Since you only visit the page to write a new post some of the time, this PR
attempts to have nothing focused initially (and require the user to click or
e.g. use 'tab' to focus the textarea).

Tested:
* /web/timeslines/home no longer autofocuses the compose box
* pressing the 'n' hotkey still focuses the compose box
* clicking 'reply' for a post still focuses the compose box
* replying to a CW'ed post still focuses the compose box
* introducing the CW field still focuses the CW field
* introducing the CW field for a reply still focuses the CW field
* removing the CW field still focuses the compose box
* /web/statuses/new still autofocuses the compose box

fixes #15862
This commit is contained in:
Arnout Engelen 2022-12-15 17:37:05 +01:00 committed by GitHub
parent 3656a6b9cc
commit 9f63c428e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 8 additions and 9 deletions

View File

@ -16,7 +16,6 @@ import PollFormContainer from '../containers/poll_form_container';
import UploadFormContainer from '../containers/upload_form_container'; import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container'; import WarningContainer from '../containers/warning_container';
import LanguageDropdown from '../containers/language_dropdown_container'; import LanguageDropdown from '../containers/language_dropdown_container';
import { isMobile } from '../../../is_mobile';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz'; import { length } from 'stringz';
import { countableText } from '../util/counter'; import { countableText } from '../util/counter';
@ -61,14 +60,14 @@ class ComposeForm extends ImmutablePureComponent {
onChangeSpoilerText: PropTypes.func.isRequired, onChangeSpoilerText: PropTypes.func.isRequired,
onPaste: PropTypes.func.isRequired, onPaste: PropTypes.func.isRequired,
onPickEmoji: PropTypes.func.isRequired, onPickEmoji: PropTypes.func.isRequired,
showSearch: PropTypes.bool, autoFocus: PropTypes.bool,
anyMedia: PropTypes.bool, anyMedia: PropTypes.bool,
isInReply: PropTypes.bool, isInReply: PropTypes.bool,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
}; };
static defaultProps = { static defaultProps = {
showSearch: false, autoFocus: false,
}; };
handleChange = (e) => { handleChange = (e) => {
@ -154,7 +153,7 @@ class ComposeForm extends ImmutablePureComponent {
// - Replying to zero or one users, places the cursor at the end of the textbox. // - Replying to zero or one users, places the cursor at the end of the textbox.
// - Replying to more than one user, selects any usernames past the first; // - Replying to more than one user, selects any usernames past the first;
// this provides a convenient shortcut to drop everyone else from the conversation. // this provides a convenient shortcut to drop everyone else from the conversation.
if (this.props.focusDate !== prevProps.focusDate) { if (this.props.focusDate && this.props.focusDate !== prevProps.focusDate) {
let selectionEnd, selectionStart; let selectionEnd, selectionStart;
if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply) { if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply) {
@ -180,7 +179,7 @@ class ComposeForm extends ImmutablePureComponent {
} else if (this.props.spoiler !== prevProps.spoiler) { } else if (this.props.spoiler !== prevProps.spoiler) {
if (this.props.spoiler) { if (this.props.spoiler) {
this.spoilerText.input.focus(); this.spoilerText.input.focus();
} else { } else if (prevProps.spoiler) {
this.autosuggestTextarea.textarea.focus(); this.autosuggestTextarea.textarea.focus();
} }
} }
@ -207,7 +206,7 @@ class ComposeForm extends ImmutablePureComponent {
} }
render () { render () {
const { intl, onPaste, showSearch } = this.props; const { intl, onPaste, autoFocus } = this.props;
const disabled = this.props.isSubmitting; const disabled = this.props.isSubmitting;
let publishText = ''; let publishText = '';
@ -257,7 +256,7 @@ class ComposeForm extends ImmutablePureComponent {
onSuggestionsClearRequested={this.onSuggestionsClearRequested} onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected} onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste} onPaste={onPaste}
autoFocus={!showSearch && !isMobile(window.innerWidth)} autoFocus={autoFocus}
> >
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} /> <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />

View File

@ -24,7 +24,6 @@ const mapStateToProps = state => ({
isEditing: state.getIn(['compose', 'id']) !== null, isEditing: state.getIn(['compose', 'id']) !== null,
isChangingUpload: state.getIn(['compose', 'is_changing_upload']), isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
isUploading: state.getIn(['compose', 'is_uploading']), isUploading: state.getIn(['compose', 'is_uploading']),
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
isInReply: state.getIn(['compose', 'in_reply_to']) !== null, isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
}); });

View File

@ -18,6 +18,7 @@ import Icon from 'mastodon/components/icon';
import { logOut } from 'mastodon/utils/log_out'; import { logOut } from 'mastodon/utils/log_out';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { isMobile } from '../../is_mobile';
const messages = defineMessages({ const messages = defineMessages({
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@ -115,7 +116,7 @@ class Compose extends React.PureComponent {
<div className='drawer__inner' onFocus={this.onFocus}> <div className='drawer__inner' onFocus={this.onFocus}>
<NavigationContainer onClose={this.onBlur} /> <NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer /> <ComposeFormContainer autoFocus={!isMobile(window.innerWidth)} />
<div className='drawer__inner__mastodon'> <div className='drawer__inner__mastodon'>
<img alt='' draggable='false' src={mascot || elephantUIPlane} /> <img alt='' draggable='false' src={mascot || elephantUIPlane} />