about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/javascript/flavours/glitch/features/composer/index.js38
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.js8
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js1
3 files changed, 42 insertions, 5 deletions
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index cf6f45b34..b823f966f 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -2,6 +2,7 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import { defineMessages } from 'react-intl';
 
 const APPROX_HASHTAG_RE = /(?:^|[^\/\)\w])#(\S+)/i;
 
@@ -49,6 +50,13 @@ import { assignHandlers } from 'flavours/glitch/util/react_helpers';
 import { wrap } from 'flavours/glitch/util/redux_helpers';
 import { privacyPreference } from 'flavours/glitch/util/privacy_preference';
 
+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' },
+});
+
 //  State mapping.
 function mapStateToProps (state) {
   const spoilersAlwaysOn = state.getIn(['local_settings', 'always_show_spoilers_field']);
@@ -93,11 +101,12 @@ function mapStateToProps (state) {
     text: state.getIn(['compose', 'text']),
     anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
     spoilersAlwaysOn: spoilersAlwaysOn,
+    mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
   };
 };
 
 //  Dispatch mapping.
-const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = (dispatch, { intl }) => ({
   onCancelReply() {
     dispatch(cancelReplyCompose());
   },
@@ -149,6 +158,13 @@ const mapDispatchToProps = (dispatch) => ({
   onSelectSuggestion(position, token, suggestion) {
     dispatch(selectComposeSuggestion(position, token, suggestion));
   },
+  onMediaDescriptionConfirm() {
+    dispatch(openModal('CONFIRM', {
+      message: intl.formatMessage(messages.missingDescriptionMessage),
+      confirm: intl.formatMessage(messages.missingDescriptionConfirm),
+      onConfirm: () => dispatch(submitCompose()),
+    }));
+  },
   onSubmit() {
     dispatch(submitCompose());
   },
@@ -212,8 +228,11 @@ const handlers = {
       onSubmit,
       isSubmitting,
       isUploading,
+      media,
       anyMedia,
       text,
+      mediaDescriptionConfirmation,
+      onMediaDescriptionConfirm,
     } = this.props;
 
     //  If something changes inside the textarea, then we update the
@@ -227,8 +246,15 @@ const handlers = {
       return;
     }
 
-    //  Submits the status.
-    if (onSubmit) {
+    // Submit unless there are media with missing descriptions
+    if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) {
+      const firstWithoutDescription = media.findIndex(item => !item.get('description'));
+      const inputs = document.querySelectorAll('.composer--upload_form--item input');
+      if (inputs.length == media.size && firstWithoutDescription !== -1) {
+        inputs[firstWithoutDescription].focus();
+      }
+      onMediaDescriptionConfirm();
+    } else if (onSubmit) {
       onSubmit();
     }
   },
@@ -495,6 +521,9 @@ Composer.propTypes = {
   suggestionToken: PropTypes.string,
   suggestions: ImmutablePropTypes.list,
   text: PropTypes.string,
+  anyMedia: PropTypes.bool,
+  spoilersAlwaysOn: PropTypes.bool,
+  mediaDescriptionConfirmation: PropTypes.bool,
 
   //  Dispatch props.
   onCancelReply: PropTypes.func,
@@ -517,8 +546,7 @@ Composer.propTypes = {
   onUndoUpload: PropTypes.func,
   onUnmount: PropTypes.func,
   onUpload: PropTypes.func,
-  anyMedia: PropTypes.bool,
-  spoilersAlwaysOn: PropTypes.bool,
+  onMediaDescriptionConfirm: PropTypes.func,
 };
 
 //  Connecting and export.
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js
index ad5c11979..1e61c67c0 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js
@@ -85,6 +85,14 @@ export default class LocalSettingsPage extends React.PureComponent {
           </LocalSettingsPageItem>
           <LocalSettingsPageItem
             settings={settings}
+            item={['confirm_missing_media_description']}
+            id='mastodon-settings--confirm_missing_media_description'
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.confirm_missing_media_description' defaultMessage='Show confirmation dialog before sending toots lacking media descriptions' />
+          </LocalSettingsPageItem>
+          <LocalSettingsPageItem
+            settings={settings}
             item={['side_arm']}
             id='mastodon-settings--side_arm'
             options={[
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index 1d24f0e9a..ebe0b1292 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -13,6 +13,7 @@ const initialState = ImmutableMap({
   side_arm_reply_mode : 'keep',
   show_reply_count : false,
   always_show_spoilers_field: false,
+  confirm_missing_media_description: false,
   collapsed : ImmutableMap({
     enabled     : true,
     auto        : ImmutableMap({