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 c8d95a11a7..ecadf21170 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx @@ -75,6 +75,9 @@ class ComposeForm extends ImmutablePureComponent { autoFocus: PropTypes.bool, withoutNavigation: PropTypes.bool, anyMedia: PropTypes.bool, + media: ImmutablePropTypes.list, + mediaDescriptionConfirmation: PropTypes.bool, + onMediaDescriptionConfirm: PropTypes.func.isRequired, isInReply: PropTypes.bool, singleColumn: PropTypes.bool, lang: PropTypes.string, @@ -100,11 +103,11 @@ class ComposeForm extends ImmutablePureComponent { handleKeyDown = (e) => { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { - this.handleSubmit(); + this.handleSubmit(e); } if (e.keyCode === 13 && e.altKey) { - this.handleSecondarySubmit(); + this.handleSecondarySubmit(e); } }; @@ -131,16 +134,22 @@ class ComposeForm extends ImmutablePureComponent { return; } - this.props.onSubmit(this.props.history || null, overridePrivacy); - if (e) { e.preventDefault(); } + + // Submit unless there are media with missing descriptions + 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'), overridePrivacy); + } else { + this.props.onSubmit(this.props.history || null, overridePrivacy); + } }; - handleSecondarySubmit = () => { + handleSecondarySubmit = (e) => { const { sideArm } = this.props; - this.handleSubmit(null, sideArm === 'none' ? null : sideArm); + this.handleSubmit(e, sideArm === 'none' ? null : sideArm); }; onSuggestionsClearRequested = () => { diff --git a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js index a371606bee..6de24cc706 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js @@ -1,3 +1,5 @@ +import { defineMessages, injectIntl } from 'react-intl'; + import { connect } from 'react-redux'; import { privacyPreference } from 'flavours/glitch/utils/privacy_preference'; @@ -12,8 +14,27 @@ import { insertEmojiCompose, uploadCompose, } from '../../../actions/compose'; +import { changeLocalSetting } from '../../../actions/local_settings'; +import { + openModal, +} from '../../../actions/modal'; import ComposeForm from '../components/compose_form'; +const messages = defineMessages({ + missingDescriptionMessage: { + id: 'confirmations.missing_media_description.message', + defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.', + }, + missingDescriptionConfirm: { + id: 'confirmations.missing_media_description.confirm', + defaultMessage: 'Send anyway', + }, + missingDescriptionEdit: { + id: 'confirmations.missing_media_description.edit', + defaultMessage: 'Edit media', + }, +}); + const sideArmPrivacy = state => { const inReplyTo = state.getIn(['compose', 'in_reply_to']); const replyPrivacy = inReplyTo ? state.getIn(['statuses', inReplyTo, 'visibility']) : null; @@ -50,9 +71,11 @@ const mapStateToProps = state => ({ isInReply: state.getIn(['compose', 'in_reply_to']) !== null, lang: state.getIn(['compose', 'language']), sideArm: sideArmPrivacy(state), + media: state.getIn(['compose', 'media_attachments']), + mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']), }); -const mapDispatchToProps = (dispatch) => ({ +const mapDispatchToProps = (dispatch, { intl }) => ({ onChange (text) { dispatch(changeCompose(text)); @@ -86,6 +109,25 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(insertEmojiCompose(position, data, needsSpace)); }, + onMediaDescriptionConfirm (routerHistory, mediaId, overridePrivacy = null) { + dispatch(openModal({ + modalType: 'CONFIRM', + modalProps: { + message: intl.formatMessage(messages.missingDescriptionMessage), + confirm: intl.formatMessage(messages.missingDescriptionConfirm), + onConfirm: () => { + dispatch(submitCompose(routerHistory, overridePrivacy)); + }, + secondary: intl.formatMessage(messages.missingDescriptionEdit), + onSecondary: () => dispatch(openModal({ + modalType: 'FOCAL_POINT', + modalProps: { id: mediaId }, + })), + onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_missing_media_description'], false)), + }, + })); + }, + }); -export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm); +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ComposeForm)); diff --git a/app/javascript/flavours/glitch/locales/en.json b/app/javascript/flavours/glitch/locales/en.json index 9c530262f2..f7d3b7f4d8 100644 --- a/app/javascript/flavours/glitch/locales/en.json +++ b/app/javascript/flavours/glitch/locales/en.json @@ -30,6 +30,9 @@ "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again", "confirmations.deprecated_settings.confirm": "Use Mastodon preferences", "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:", + "confirmations.missing_media_description.confirm": "Send anyway", + "confirmations.missing_media_description.edit": "Edit media", + "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.", "confirmations.unfilter.author": "Author", "confirmations.unfilter.confirm": "Show", "confirmations.unfilter.edit_filter": "Edit filter",