diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 707c21c380..07f60b9270 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -251,9 +251,9 @@ export function submitCompose(routerHistory) { return; } - // To make the app more responsive, immediately get the status into the columns - - const insertIfOnline = (timelineId) => { + // To make the app more responsive, immediately push the status + // into the columns + const insertIfOnline = timelineId => { const timeline = getState().getIn(['timelines', timelineId]); if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) { @@ -662,8 +662,9 @@ export function selectComposeSuggestion(position, token, suggestion, path) { return (dispatch, getState) => { let completion; if (suggestion.type === 'emoji') { - dispatch(useEmoji(suggestion)); completion = suggestion.native || suggestion.colons; + + dispatch(useEmoji(suggestion)); } else if (suggestion.type === 'hashtag') { completion = `#${suggestion.name}`; } else if (suggestion.type === 'account') { diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx index 47b070da24..4ec2b81d11 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx @@ -56,14 +56,14 @@ class ComposeForm extends ImmutablePureComponent { isChangingUpload: PropTypes.bool, isEditing: PropTypes.bool, isUploading: PropTypes.bool, - onChange: PropTypes.func, - onSubmit: PropTypes.func, - onClearSuggestions: PropTypes.func, - onFetchSuggestions: PropTypes.func, - onSuggestionSelected: PropTypes.func, - onChangeSpoilerText: PropTypes.func, - onPaste: PropTypes.func, - onPickEmoji: PropTypes.func, + onChange: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + onClearSuggestions: PropTypes.func.isRequired, + onFetchSuggestions: PropTypes.func.isRequired, + onSuggestionSelected: PropTypes.func.isRequired, + onChangeSpoilerText: PropTypes.func.isRequired, + onPaste: PropTypes.func.isRequired, + onPickEmoji: PropTypes.func.isRequired, showSearch: PropTypes.bool, anyMedia: PropTypes.bool, isInReply: PropTypes.bool, @@ -77,9 +77,9 @@ class ComposeForm extends ImmutablePureComponent { spoilersAlwaysOn: PropTypes.bool, mediaDescriptionConfirmation: PropTypes.bool, preselectOnReply: PropTypes.bool, - onChangeSpoilerness: PropTypes.func, - onChangeVisibility: PropTypes.func, - onMediaDescriptionConfirm: PropTypes.func, + onChangeSpoilerness: PropTypes.func.isRequired, + onChangeVisibility: PropTypes.func.isRequired, + onMediaDescriptionConfirm: PropTypes.func.isRequired, ...WithOptionalRouterPropTypes }; @@ -100,6 +100,16 @@ class ComposeForm extends ImmutablePureComponent { this.props.onChange(e.target.value); }; + handleKeyDown = (e) => { + if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { + this.handleSubmit(); + } + + if (e.keyCode === 13 && e.altKey) { + this.handleSecondarySubmit(); + } + }; + getFulltextForCharacterCounting = () => { return [ this.props.spoiler? this.props.spoilerText: '', @@ -116,14 +126,6 @@ class ComposeForm extends ImmutablePureComponent { }; handleSubmit = (overriddenVisibility = null) => { - const { - onSubmit, - media, - mediaDescriptionConfirmation, - onMediaDescriptionConfirm, - onChangeVisibility, - } = this.props; - if (this.props.text !== this.textareaRef.current.value) { // Something changed the text inside the textarea (e.g. browser extensions like Grammarly) // Update the state to match the current text @@ -135,35 +137,14 @@ class ComposeForm extends ImmutablePureComponent { } // Submit unless there are media with missing descriptions - if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) { - const firstWithoutDescription = media.find(item => !item.get('description')); - onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility); - } else if (onSubmit) { - if (onChangeVisibility && overriddenVisibility) { - onChangeVisibility(overriddenVisibility); + if (this.props.mediaDescriptionConfirmation && this.props.media && this.props.media.some(item => !item.get('description'))) { + const firstWithoutDescription = this.props.media.find(item => !item.get('description')); + this.props.onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility); + } else { + if (overriddenVisibility) { + this.props.onChangeVisibility(overriddenVisibility); } - onSubmit(this.props.history || null); - } - }; - - // Changes the text value of the spoiler. - handleChangeSpoiler = ({ target: { value } }) => { - const { onChangeSpoilerText } = this.props; - if (onChangeSpoilerText) { - onChangeSpoilerText(value); - } - }; - - setRef = c => { - this.composeForm = c; - }; - - // Inserts an emoji at the caret. - handleEmojiPick = (data) => { - const position = this.textareaRef.current.selectionStart; - - if (this.props.onPickEmoji) { - this.props.onPickEmoji(position, data); + this.props.onSubmit(this.props.history || null); } }; @@ -175,30 +156,24 @@ class ComposeForm extends ImmutablePureComponent { this.handleSubmit(sideArm === 'none' ? null : sideArm); }; - // Selects a suggestion from the autofill. - handleSuggestionSelected = (tokenStart, token, value) => { + onSuggestionsClearRequested = () => { + this.props.onClearSuggestions(); + }; + + onSuggestionsFetchRequested = (token) => { + this.props.onFetchSuggestions(token); + }; + + onSuggestionSelected = (tokenStart, token, value) => { this.props.onSuggestionSelected(tokenStart, token, value, ['text']); }; - handleSpoilerSuggestionSelected = (tokenStart, token, value) => { + onSpoilerSuggestionSelected = (tokenStart, token, value) => { this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']); }; - handleKeyDown = (e) => { - if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { - this.handleSubmit(); - } - - if (e.keyCode === 13 && e.altKey) { - this.handleSecondarySubmit(); - } - }; - - // Sets a reference to the CW field. - handleRefSpoilerText = (spoilerComponent) => { - if (spoilerComponent) { - this.spoilerText = spoilerComponent.input; - } + handleChangeSpoilerText = (e) => { + this.props.onChangeSpoilerText(e.target.value); }; handleFocus = () => { @@ -222,120 +197,99 @@ class ComposeForm extends ImmutablePureComponent { this._updateFocusAndSelection(prevProps); } - // This statement does several things: - // - If we're beginning a reply, and, - // - 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; this provides a convenient shortcut to drop - // everyone else from the conversation. _updateFocusAndSelection = (prevProps) => { - const { - spoilerText, - } = this; - const { - focusDate, - caretPosition, - isSubmitting, - preselectDate, - text, - preselectOnReply, - singleColumn, - } = this.props; - let selectionEnd, selectionStart; + // This statement does several things: + // - If we're beginning a reply, and, + // - 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; + // this provides a convenient shortcut to drop everyone else from the conversation. + if (this.props.focusDate && this.props.focusDate !== prevProps.focusDate) { + let selectionEnd, selectionStart; - // Caret/selection handling. - if (focusDate !== prevProps.focusDate) { - switch (true) { - case preselectDate !== prevProps.preselectDate && this.props.isInReply && preselectOnReply: - selectionStart = text.search(/\s/) + 1; - selectionEnd = text.length; - break; - case !isNaN(caretPosition) && caretPosition !== null: - selectionStart = selectionEnd = caretPosition; - break; - default: - selectionStart = selectionEnd = text.length; - } - if (this.textareaRef.current) { - // Because of the wicg-inert polyfill, the activeElement may not be - // immediately selectable, we have to wait for observers to run, as - // described in https://github.com/WICG/inert#performance-and-gotchas - Promise.resolve().then(() => { - this.textareaRef.current.setSelectionRange(selectionStart, selectionEnd); - this.textareaRef.current.focus(); - if (!singleColumn) this.textareaRef.current.scrollIntoView(); - this.setState({ highlighted: true }); - this.timeout = setTimeout(() => this.setState({ highlighted: false }), 700); - }).catch(console.error); + if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply && this.props.preselectOnReply) { + selectionEnd = this.props.text.length; + selectionStart = this.props.text.search(/\s/) + 1; + } else if (typeof this.props.caretPosition === 'number') { + selectionStart = this.props.caretPosition; + selectionEnd = this.props.caretPosition; + } else { + selectionEnd = this.props.text.length; + selectionStart = selectionEnd; } - // Refocuses the textarea after submitting. - } else if (this.textareaRef.current && prevProps.isSubmitting && !isSubmitting) { + // Because of the wicg-inert polyfill, the activeElement may not be + // immediately selectable, we have to wait for observers to run, as + // described in https://github.com/WICG/inert#performance-and-gotchas + Promise.resolve().then(() => { + this.textareaRef.current.setSelectionRange(selectionStart, selectionEnd); + this.textareaRef.current.focus(); + if (!this.props.singleColumn) this.textareaRef.current.scrollIntoView(); + this.setState({ highlighted: true }); + this.timeout = setTimeout(() => this.setState({ highlighted: false }), 700); + }).catch(console.error); + } else if(prevProps.isSubmitting && !this.props.isSubmitting) { this.textareaRef.current.focus(); } else if (this.props.spoiler !== prevProps.spoiler) { if (this.props.spoiler) { - if (spoilerText) { - spoilerText.focus(); - } - } else { - if (this.textareaRef.current) { - this.textareaRef.current.focus(); - } + this.spoilerText.input.focus(); + } else if (prevProps.spoiler) { + this.textareaRef.current.focus(); } } }; + setSpoilerText = (c) => { + this.spoilerText = c; + }; + + setRef = c => { + this.composeForm = c; + }; + + handleEmojiPick = (data) => { + const position = this.textareaRef.current.selectionStart; + + this.props.onPickEmoji(position, data); + }; render () { const { - handleEmojiPick, - handleSecondarySubmit, - handleSubmit, - } = this; - const { - advancedOptions, intl, + advancedOptions, isSubmitting, layout, onChangeSpoilerness, - onClearSuggestions, - onFetchSuggestions, onPaste, privacy, sensitive, showSearch, sideArm, - spoiler, - spoilerText, - suggestions, spoilersAlwaysOn, isEditing, } = this.props; const { highlighted } = this.state; - - const countText = this.getFulltextForCharacterCounting(); + const disabled = this.props.isSubmitting; return ( -