From 9ea1df2fb17c26e8f21e3452a3c5271388b57d36 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 30 Jun 2021 14:53:29 -0500 Subject: [PATCH] Scheduled statuses validation --- app/soapbox/actions/compose.js | 16 ++++++++++++++++ .../features/compose/components/schedule_form.js | 3 ++- app/soapbox/reducers/compose.js | 6 +----- app/styles/components/datepicker.scss | 4 ++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/soapbox/actions/compose.js b/app/soapbox/actions/compose.js index 8c0f1d3c4..3a95cddd6 100644 --- a/app/soapbox/actions/compose.js +++ b/app/soapbox/actions/compose.js @@ -15,6 +15,7 @@ import { getFeatures } from 'soapbox/utils/features'; import { uploadMedia } from './media'; import { isLoggedIn } from 'soapbox/utils/auth'; import { createStatus } from './statuses'; +import snackbar from 'soapbox/actions/snackbar'; let cancelFetchComposeSuggestionsAccounts; @@ -71,6 +72,7 @@ export const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE'; const messages = defineMessages({ uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' }, uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, + scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' }, }); const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1); @@ -170,6 +172,15 @@ const needsDescriptions = state => { return missingDescriptionModal && hasMissing; }; +const validateSchedule = state => { + const schedule = state.getIn(['compose', 'schedule']); + if (!schedule) return true; + + const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); + + return schedule.getTime() > fiveMinutesFromNow.getTime(); +}; + export function submitCompose(routerHistory, force = false) { return function(dispatch, getState) { if (!isLoggedIn(getState)) return; @@ -178,6 +189,11 @@ export function submitCompose(routerHistory, force = false) { const status = state.getIn(['compose', 'text'], ''); const media = state.getIn(['compose', 'media_attachments']); + if (!validateSchedule(state)) { + dispatch(snackbar.error(messages.scheduleError)); + return; + } + if ((!status || !status.length) && media.size === 0) { return; } diff --git a/app/soapbox/features/compose/components/schedule_form.js b/app/soapbox/features/compose/components/schedule_form.js index bb6304a9f..0cf3a47fd 100644 --- a/app/soapbox/features/compose/components/schedule_form.js +++ b/app/soapbox/features/compose/components/schedule_form.js @@ -8,6 +8,7 @@ import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import IconButton from 'soapbox/components/icon_button'; import { removeSchedule } from 'soapbox/actions/compose'; +import classNames from 'classnames'; const messages = defineMessages({ schedule: { id: 'schedule.post_time', defaultMessage: 'Post Date/Time' }, @@ -66,7 +67,7 @@ class ScheduleForm extends React.Component { const { intl, scheduledAt } = this.props; return ( -
+
diff --git a/app/soapbox/reducers/compose.js b/app/soapbox/reducers/compose.js index fa11f62f5..b7e496ebe 100644 --- a/app/soapbox/reducers/compose.js +++ b/app/soapbox/reducers/compose.js @@ -84,10 +84,6 @@ const initialPoll = ImmutableMap({ multiple: false, }); -const initialSchedule = new Date(); -initialSchedule.setDate(initialSchedule.getDate() - 1); - - function statusToTextMentions(state, status, account) { const author = status.getIn(['account', 'acct']); const mentions = status.get('mentions', []).map(m => m.get('acct')); @@ -407,7 +403,7 @@ export default function compose(state = initialState, action) { case COMPOSE_POLL_REMOVE: return state.set('poll', null); case COMPOSE_SCHEDULE_ADD: - return state.set('schedule', initialSchedule); + return state.set('schedule', new Date()); case COMPOSE_SCHEDULE_SET: return state.set('schedule', action.date); case COMPOSE_SCHEDULE_REMOVE: diff --git a/app/styles/components/datepicker.scss b/app/styles/components/datepicker.scss index 9a86981a3..ef8483b97 100644 --- a/app/styles/components/datepicker.scss +++ b/app/styles/components/datepicker.scss @@ -20,6 +20,10 @@ &__cancel { padding-left: 10px; } + + &--error .react-datepicker__input-container { + border-color: $error-red !important; + } } .datepicker .react-datepicker {