diff --git a/.env.example b/.env.example deleted file mode 100644 index b116e6d40..000000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -NODE_ENV=development -# BACKEND_URL="https://example.com" -# PROXY_HTTPS_INSECURE=false diff --git a/app/soapbox/actions/compose.js b/app/soapbox/actions/compose.js index bd91daaae..15a5bb94a 100644 --- a/app/soapbox/actions/compose.js +++ b/app/soapbox/actions/compose.js @@ -164,6 +164,34 @@ export function submitCompose(routerHistory, group) { return function(dispatch, getState) { if (!isLoggedIn(getState)) return; + function onModalSubmitCompose () { + dispatch(submitComposeRequest()); + dispatch(closeModal()); + + api(getState).post('/api/v1/statuses', { + status, + in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), + media_ids: media.map(item => item.get('id')), + sensitive: getState().getIn(['compose', 'sensitive']), + spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''), + visibility: getState().getIn(['compose', 'privacy']), + content_type: getState().getIn(['compose', 'content_type']), + poll: getState().getIn(['compose', 'poll'], null), + scheduled_at: getState().getIn(['compose', 'schedule'], null), + }, { + headers: { + 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), + }, + }).then(function(response) { + if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) { + routerHistory.push('/messages'); + } + handleComposeSubmit(dispatch, getState, response, status); + }).catch(function(error) { + dispatch(submitComposeFail(error)); + }); + } + const status = getState().getIn(['compose', 'text'], ''); const media = getState().getIn(['compose', 'media_attachments']); @@ -171,31 +199,14 @@ export function submitCompose(routerHistory, group) { return; } - dispatch(submitComposeRequest()); - dispatch(closeModal()); + const missingDescriptionModal = getSettings(getState()).get('missingDescriptionModal'); - api(getState).post('/api/v1/statuses', { - status, - in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), - media_ids: media.map(item => item.get('id')), - sensitive: getState().getIn(['compose', 'sensitive']), - spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''), - visibility: getState().getIn(['compose', 'privacy']), - content_type: getState().getIn(['compose', 'content_type']), - poll: getState().getIn(['compose', 'poll'], null), - scheduled_at: getState().getIn(['compose', 'schedule'], null), - }, { - headers: { - 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), - }, - }).then(function(response) { - if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) { - routerHistory.push('/messages'); - } - handleComposeSubmit(dispatch, getState, response, status); - }).catch(function(error) { - dispatch(submitComposeFail(error)); - }); + if (missingDescriptionModal && media.filter(item => !item.get('description')).size) { + dispatch(openModal('MISSING_DESCRIPTION', { + some: media.filter(item => !item.get('description')).size !== media.size, + onContinue: () => onModalSubmitCompose(), + })); + } else onModalSubmitCompose(); }; }; diff --git a/app/soapbox/actions/settings.js b/app/soapbox/actions/settings.js index d3d572715..d857fd1e9 100644 --- a/app/soapbox/actions/settings.js +++ b/app/soapbox/actions/settings.js @@ -21,6 +21,7 @@ export const defaultSettings = ImmutableMap({ unfollowModal: false, boostModal: false, deleteModal: true, + missingDescriptionModal: false, defaultPrivacy: 'public', defaultContentType: 'text/plain', themeMode: 'light', diff --git a/app/soapbox/features/preferences/index.js b/app/soapbox/features/preferences/index.js index dd1072c49..ad3ccc4f7 100644 --- a/app/soapbox/features/preferences/index.js +++ b/app/soapbox/features/preferences/index.js @@ -202,6 +202,10 @@ class Preferences extends ImmutablePureComponent { label={} path={['deleteModal']} /> + } + path={['missingDescriptionModal']} + /> diff --git a/app/soapbox/features/ui/components/missing_description_modal.js b/app/soapbox/features/ui/components/missing_description_modal.js new file mode 100644 index 000000000..f00cddbe9 --- /dev/null +++ b/app/soapbox/features/ui/components/missing_description_modal.js @@ -0,0 +1,56 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import Button from '../../../components/button'; + +export default @injectIntl +class MissingDescriptionModal extends React.PureComponent { + + static propTypes = { + some: PropTypes.bool, + onClose: PropTypes.func, + onContinue: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + componentDidMount() { + this.button.focus(); + } + + handleContinue = () => { + this.props.onClose(); + this.props.onContinue(); + } + + handleCancel = () => { + this.props.onClose(); + } + + setRef = (c) => { + this.button = c; + } + + render() { + const { some } = this.props; + + return ( +
+
+ {some + ? + : } +
+ +
+ + +
+
+ ); + } + +} diff --git a/app/soapbox/features/ui/components/modal_root.js b/app/soapbox/features/ui/components/modal_root.js index b3f7f8681..657df1a26 100644 --- a/app/soapbox/features/ui/components/modal_root.js +++ b/app/soapbox/features/ui/components/modal_root.js @@ -9,6 +9,7 @@ import MediaModal from './media_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import ConfirmationModal from './confirmation_modal'; +import MissingDescriptionModal from './missing_description_modal'; import FocalPointModal from './focal_point_modal'; import HotkeysModal from './hotkeys_modal'; import ComposeModal from './compose_modal'; @@ -28,6 +29,7 @@ const MODAL_COMPONENTS = { 'VIDEO': () => Promise.resolve({ default: VideoModal }), 'BOOST': () => Promise.resolve({ default: BoostModal }), 'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }), + 'MISSING_DESCRIPTION': () => Promise.resolve({ default: MissingDescriptionModal }), 'MUTE': MuteModal, 'REPORT': ReportModal, 'ACTIONS': () => Promise.resolve({ default: ActionsModal }), diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 42c75dd6b..1c820b85f 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -421,6 +421,10 @@ "mfa.setup_otp_title": "Wyłączono OTP", "mfa.setup_recoverycodes": "Kody przywracania", "mfa.setup_warning": "Zapisz te kody gdzieś w bezpiecznym miejscu – jeżeli tego nie zrobisz, już ich nie zobaczysz. Jeśli utracisz dostęp do aplikacji 2FA i tych kodów, stracisz dostęp do swojego konta.", + "missing_description_modal.all": "Zamierzasz opublikować media, które nie zawierają opisu. Czy na pewno chcesz kontynuować?", + "missing_description_modal.cancel": "Anuluj", + "missing_description_modal.continue": "Kontynuuj", + "missing_description_modal.some": "Niektóre media które zamierzasz opublikować nie zawierają opisu. Czy na pewno chcesz kontynuować?", "missing_indicator.label": "Nie znaleziono", "missing_indicator.sublabel": "Nie można odnaleźć tego zasobu", "morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.", @@ -497,6 +501,7 @@ "preferences.fields.halloween_label": "Tryb Halloween", "preferences.fields.language_label": "Język", "preferences.fields.media_display_label": "Wyświetlanie zawartości multimedialnej", + "preferences.fields.missing_description_modal_label": "Pytaj o potwierdzenie przed wysłaniem zawartości multimedialnej bez opisu", "preferences.fields.privacy_label": "Prywatność wpisów", "preferences.fields.reduce_motion_label": "Ogranicz ruch w animacjach", "preferences.fields.system_font_label": "Używaj domyślnej czcionki systemu",