kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'develop' into 'develop'
Confirmation modals about missing image descriptions (configurable) See merge request soapbox-pub/soapbox-fe!544datepicker-css
commit
58f3107151
|
@ -164,6 +164,34 @@ export function submitCompose(routerHistory, group) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
if (!isLoggedIn(getState)) return;
|
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 status = getState().getIn(['compose', 'text'], '');
|
||||||
const media = getState().getIn(['compose', 'media_attachments']);
|
const media = getState().getIn(['compose', 'media_attachments']);
|
||||||
|
|
||||||
|
@ -171,31 +199,13 @@ export function submitCompose(routerHistory, group) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(submitComposeRequest());
|
const missingDescriptionModal = getSettings(getState()).get('missingDescriptionModal');
|
||||||
dispatch(closeModal());
|
|
||||||
|
|
||||||
api(getState).post('/api/v1/statuses', {
|
if (missingDescriptionModal && media.filter(item => !item.get('description')).size) {
|
||||||
status,
|
dispatch(openModal('MISSING_DESCRIPTION', {
|
||||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
onContinue: () => onModalSubmitCompose(),
|
||||||
media_ids: media.map(item => item.get('id')),
|
}));
|
||||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
} else onModalSubmitCompose();
|
||||||
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));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ export const defaultSettings = ImmutableMap({
|
||||||
unfollowModal: false,
|
unfollowModal: false,
|
||||||
boostModal: false,
|
boostModal: false,
|
||||||
deleteModal: true,
|
deleteModal: true,
|
||||||
|
missingDescriptionModal: false,
|
||||||
defaultPrivacy: 'public',
|
defaultPrivacy: 'public',
|
||||||
defaultContentType: 'text/plain',
|
defaultContentType: 'text/plain',
|
||||||
themeMode: 'light',
|
themeMode: 'light',
|
||||||
|
|
|
@ -20,41 +20,34 @@ class ScheduleForm extends React.Component {
|
||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
setSchedule(date)
|
setSchedule(date) {
|
||||||
{
|
|
||||||
this.setState({ schedule: date });
|
this.setState({ schedule: date });
|
||||||
this.props.onSchedule(date);
|
this.props.onSchedule(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
openDatePicker(datePicker)
|
openDatePicker(datePicker) {
|
||||||
{
|
if (!datePicker) {
|
||||||
if (!datePicker)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
datePicker.setOpen(true);
|
datePicker.setOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount()
|
componentDidMount() {
|
||||||
{
|
|
||||||
this.setState({ schedule: this.props.schedule });
|
this.setState({ schedule: this.props.schedule });
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props)
|
constructor(props) {
|
||||||
{
|
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.setSchedule = this.setSchedule.bind(this);
|
this.setSchedule = this.setSchedule.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
isCurrentOrFutureDate(date)
|
isCurrentOrFutureDate(date) {
|
||||||
{
|
|
||||||
return date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
|
return date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
isFiveMinutesFromNow(time)
|
isFiveMinutesFromNow(time) {
|
||||||
{
|
|
||||||
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); // now, plus five minutes (Pleroma won't schedule posts )
|
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); // now, plus five minutes (Pleroma won't schedule posts )
|
||||||
const selectedDate = new Date(time);
|
const selectedDate = new Date(time);
|
||||||
|
|
||||||
|
@ -62,8 +55,7 @@ class ScheduleForm extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.active || !this.state)
|
if (!this.props.active || !this.state) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,10 @@ class Preferences extends ImmutablePureComponent {
|
||||||
label={<FormattedMessage id='preferences.fields.delete_modal_label' defaultMessage='Show confirmation dialog before deleting a post' />}
|
label={<FormattedMessage id='preferences.fields.delete_modal_label' defaultMessage='Show confirmation dialog before deleting a post' />}
|
||||||
path={['deleteModal']}
|
path={['deleteModal']}
|
||||||
/>
|
/>
|
||||||
|
<SettingsCheckbox
|
||||||
|
label={<FormattedMessage id='preferences.fields.missing_description_modal_label' defaultMessage='Show confirmation dialog before sending a post without media descriptions' />}
|
||||||
|
path={['missingDescriptionModal']}
|
||||||
|
/>
|
||||||
</FieldsGroup>
|
</FieldsGroup>
|
||||||
|
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
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 = {
|
||||||
|
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() {
|
||||||
|
return (
|
||||||
|
<div className='modal-root__modal confirmation-modal'>
|
||||||
|
<div className='confirmation-modal__container'>
|
||||||
|
<FormattedMessage id='missing_description_modal.text' defaultMessage='You have not entered a description for all attachments. Continue anyway?' />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='confirmation-modal__action-bar'>
|
||||||
|
<Button onClick={this.handleCancel} className='confirmation-modal__cancel-button' ref={this.setRef}>
|
||||||
|
<FormattedMessage id='missing_description_modal.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={this.handleContinue}>
|
||||||
|
<FormattedMessage id='missing_description_modal.continue' defaultMessage='Post' />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import MediaModal from './media_modal';
|
||||||
import VideoModal from './video_modal';
|
import VideoModal from './video_modal';
|
||||||
import BoostModal from './boost_modal';
|
import BoostModal from './boost_modal';
|
||||||
import ConfirmationModal from './confirmation_modal';
|
import ConfirmationModal from './confirmation_modal';
|
||||||
|
import MissingDescriptionModal from './missing_description_modal';
|
||||||
import FocalPointModal from './focal_point_modal';
|
import FocalPointModal from './focal_point_modal';
|
||||||
import HotkeysModal from './hotkeys_modal';
|
import HotkeysModal from './hotkeys_modal';
|
||||||
import ComposeModal from './compose_modal';
|
import ComposeModal from './compose_modal';
|
||||||
|
@ -28,6 +29,7 @@ const MODAL_COMPONENTS = {
|
||||||
'VIDEO': () => Promise.resolve({ default: VideoModal }),
|
'VIDEO': () => Promise.resolve({ default: VideoModal }),
|
||||||
'BOOST': () => Promise.resolve({ default: BoostModal }),
|
'BOOST': () => Promise.resolve({ default: BoostModal }),
|
||||||
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
|
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
|
||||||
|
'MISSING_DESCRIPTION': () => Promise.resolve({ default: MissingDescriptionModal }),
|
||||||
'MUTE': MuteModal,
|
'MUTE': MuteModal,
|
||||||
'REPORT': ReportModal,
|
'REPORT': ReportModal,
|
||||||
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
|
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
|
||||||
|
|
|
@ -421,6 +421,9 @@
|
||||||
"mfa.setup_otp_title": "Wyłączono OTP",
|
"mfa.setup_otp_title": "Wyłączono OTP",
|
||||||
"mfa.setup_recoverycodes": "Kody przywracania",
|
"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.",
|
"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.cancel": "Anuluj",
|
||||||
|
"missing_description_modal.continue": "Opublikuj",
|
||||||
|
"missing_description_modal.text": "Nie podałeś(-aś) opisu dla wszystkich załączników. Czy na pewno chcesz kontynuować?",
|
||||||
"missing_indicator.label": "Nie znaleziono",
|
"missing_indicator.label": "Nie znaleziono",
|
||||||
"missing_indicator.sublabel": "Nie można odnaleźć tego zasobu",
|
"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.",
|
"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 +500,7 @@
|
||||||
"preferences.fields.halloween_label": "Tryb Halloween",
|
"preferences.fields.halloween_label": "Tryb Halloween",
|
||||||
"preferences.fields.language_label": "Język",
|
"preferences.fields.language_label": "Język",
|
||||||
"preferences.fields.media_display_label": "Wyświetlanie zawartości multimedialnej",
|
"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.privacy_label": "Prywatność wpisów",
|
||||||
"preferences.fields.reduce_motion_label": "Ogranicz ruch w animacjach",
|
"preferences.fields.reduce_motion_label": "Ogranicz ruch w animacjach",
|
||||||
"preferences.fields.system_font_label": "Używaj domyślnej czcionki systemu",
|
"preferences.fields.system_font_label": "Używaj domyślnej czcionki systemu",
|
||||||
|
|
Ładowanie…
Reference in New Issue