From 053e214c863a6b3c0fac00d96c5da6dbb295bb4c Mon Sep 17 00:00:00 2001 From: infinite-persistence <64950861+infinite-persistence@users.noreply.github.com> Date: Thu, 23 Jun 2022 19:27:08 +0800 Subject: [PATCH] `PublishReleaseTime` widget improvements (#1740) * PublishReleaseDate: improve calendar error handling Ticket: 1738 - Report invalid `minute` and `day`. The 3rd-party widget auto-corrects the other fields. Don't think there is a way to make it autocorrect for all. - Report invalid range (cannot set to future date). * Block form on releaseDate error instead of silently sending last valid value which does not tally with what's on screen. --- flow-typed/publish.js | 3 ++ static/app-strings.json | 2 + ui/component/publishForm/view.jsx | 3 ++ ui/component/publishReleaseDate/index.js | 1 + ui/component/publishReleaseDate/view.jsx | 60 +++++++++++++++++++++++- ui/redux/reducers/publish.js | 1 + ui/scss/component/_form-field.scss | 35 ++++++++++---- 7 files changed, 94 insertions(+), 11 deletions(-) diff --git a/flow-typed/publish.js b/flow-typed/publish.js index 237380016..5f9daa4f1 100644 --- a/flow-typed/publish.js +++ b/flow-typed/publish.js @@ -14,6 +14,9 @@ declare type UpdatePublishFormData = { thumbnailError?: boolean, description?: string, language?: string, + releaseTime?: number, + releaseTimeEdited?: number, + releaseTimeError?: string, channel?: string, channelId?: string, name?: string, diff --git a/static/app-strings.json b/static/app-strings.json index b0e70cf25..1e3b467ef 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1939,6 +1939,8 @@ "Release date": "Release date", "Scheduled for": "Scheduled for", "Set custom release date": "Set custom release date", + "Cannot set to a future date.": "Cannot set to a future date.", + "Invalid date/time.": "Invalid date/time.", "Now": "Now", "Set to current date and time": "Set to current date and time", "Remove custom release date": "Remove custom release date", diff --git a/ui/component/publishForm/view.jsx b/ui/component/publishForm/view.jsx index 4fb02c77d..cffc255ab 100644 --- a/ui/component/publishForm/view.jsx +++ b/ui/component/publishForm/view.jsx @@ -54,6 +54,7 @@ type Props = { thumbnailPath: ?string, description: ?string, language: string, + releaseTimeError: ?string, nsfw: boolean, contentIsFree: boolean, fee: { @@ -109,6 +110,7 @@ function PublishForm(props: Props) { title, bid, bidError, + releaseTimeError, uploadThumbnailStatus, resetThumbnailStatus, updatePublishForm, @@ -236,6 +238,7 @@ function PublishForm(props: Props) { bid && thumbnail && !bidError && + !releaseTimeError && !emptyPostError && !(thumbnailError && !thumbnailUploaded) && !(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS); diff --git a/ui/component/publishReleaseDate/index.js b/ui/component/publishReleaseDate/index.js index 962afce19..e7a1e4fee 100644 --- a/ui/component/publishReleaseDate/index.js +++ b/ui/component/publishReleaseDate/index.js @@ -6,6 +6,7 @@ import PublishReleaseDate from './view'; const select = (state) => ({ releaseTime: makeSelectPublishFormValue('releaseTime')(state), releaseTimeEdited: makeSelectPublishFormValue('releaseTimeEdited')(state), + releaseTimeError: makeSelectPublishFormValue('releaseTimeError')(state), }); const perform = (dispatch) => ({ diff --git a/ui/component/publishReleaseDate/view.jsx b/ui/component/publishReleaseDate/view.jsx index 1abf0f84b..8639bcd13 100644 --- a/ui/component/publishReleaseDate/view.jsx +++ b/ui/component/publishReleaseDate/view.jsx @@ -14,6 +14,7 @@ function dateToLinuxTimestamp(date: Date) { const NOW = 'now'; const DEFAULT = 'default'; const RESET_TO_ORIGINAL = 'reset-to-original'; +const FUTURE_DATE_ERROR = 'Cannot set to a future date.'; type Props = { releaseTime: ?number, @@ -35,6 +36,7 @@ const PublishReleaseDate = (props: Props) => { } = props; const maxDate = useMaxDate ? new Date() : undefined; const [date, setDate] = React.useState(releaseTime ? linuxTimestampToDate(releaseTime) : new Date()); + const [error, setError] = React.useState([]); const isNew = releaseTime === undefined; const isEdit = !isNew || allowDefault === false; @@ -45,7 +47,38 @@ const PublishReleaseDate = (props: Props) => { // const showDatePicker = isEdit || releaseTimeEdited !== undefined; const showDatePicker = true; + const updateError = (action, error) => { + switch (action) { + case 'remove': + setError((prev) => prev.filter((x) => x !== error)); + break; + + case 'clear': + setError([]); + break; + + case 'add': + setError((prev) => { + const nextError = prev.slice(); + if (!nextError.includes(error)) { + nextError.push(error); + return nextError; + } + return prev; + }); + break; + } + }; + const onDateTimePickerChanged = (value) => { + const isValueInFuture = maxDate && value && value.getTime() > maxDate.getTime(); + if (isValueInFuture) { + updateError('add', FUTURE_DATE_ERROR); + return; + } + + updateError('remove', FUTURE_DATE_ERROR); + if (value) { newDate(value); } else { @@ -60,6 +93,8 @@ const PublishReleaseDate = (props: Props) => { }; function newDate(value: string | Date) { + updateError('clear', FUTURE_DATE_ERROR); + switch (value) { case NOW: const newDate = new Date(); @@ -88,23 +123,38 @@ const PublishReleaseDate = (props: Props) => { } } + function handleBlur(event) { + if (event.target.name === 'minute' || event.target.name === 'day') { + const validity = event?.target?.validity; + if (validity.rangeOverflow || validity.rangeUnderflow) { + updateError('add', event.target.name); + } else if (error.includes(event.target.name)) { + updateError('remove', event.target.name); + } + } + } + useEffect(() => { return () => { updatePublishForm({ releaseTimeEdited: undefined }); }; }, []); + useEffect(() => { + updatePublishForm({ releaseTimeError: error.join(';') }); + }, [error]); + return (