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.
This commit is contained in:
parent
b6f9b0e6d4
commit
053e214c86
7 changed files with 94 additions and 11 deletions
3
flow-typed/publish.js
vendored
3
flow-typed/publish.js
vendored
|
@ -14,6 +14,9 @@ declare type UpdatePublishFormData = {
|
||||||
thumbnailError?: boolean,
|
thumbnailError?: boolean,
|
||||||
description?: string,
|
description?: string,
|
||||||
language?: string,
|
language?: string,
|
||||||
|
releaseTime?: number,
|
||||||
|
releaseTimeEdited?: number,
|
||||||
|
releaseTimeError?: string,
|
||||||
channel?: string,
|
channel?: string,
|
||||||
channelId?: string,
|
channelId?: string,
|
||||||
name?: string,
|
name?: string,
|
||||||
|
|
|
@ -1939,6 +1939,8 @@
|
||||||
"Release date": "Release date",
|
"Release date": "Release date",
|
||||||
"Scheduled for": "Scheduled for",
|
"Scheduled for": "Scheduled for",
|
||||||
"Set custom release date": "Set custom release date",
|
"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",
|
"Now": "Now",
|
||||||
"Set to current date and time": "Set to current date and time",
|
"Set to current date and time": "Set to current date and time",
|
||||||
"Remove custom release date": "Remove custom release date",
|
"Remove custom release date": "Remove custom release date",
|
||||||
|
|
|
@ -54,6 +54,7 @@ type Props = {
|
||||||
thumbnailPath: ?string,
|
thumbnailPath: ?string,
|
||||||
description: ?string,
|
description: ?string,
|
||||||
language: string,
|
language: string,
|
||||||
|
releaseTimeError: ?string,
|
||||||
nsfw: boolean,
|
nsfw: boolean,
|
||||||
contentIsFree: boolean,
|
contentIsFree: boolean,
|
||||||
fee: {
|
fee: {
|
||||||
|
@ -109,6 +110,7 @@ function PublishForm(props: Props) {
|
||||||
title,
|
title,
|
||||||
bid,
|
bid,
|
||||||
bidError,
|
bidError,
|
||||||
|
releaseTimeError,
|
||||||
uploadThumbnailStatus,
|
uploadThumbnailStatus,
|
||||||
resetThumbnailStatus,
|
resetThumbnailStatus,
|
||||||
updatePublishForm,
|
updatePublishForm,
|
||||||
|
@ -236,6 +238,7 @@ function PublishForm(props: Props) {
|
||||||
bid &&
|
bid &&
|
||||||
thumbnail &&
|
thumbnail &&
|
||||||
!bidError &&
|
!bidError &&
|
||||||
|
!releaseTimeError &&
|
||||||
!emptyPostError &&
|
!emptyPostError &&
|
||||||
!(thumbnailError && !thumbnailUploaded) &&
|
!(thumbnailError && !thumbnailUploaded) &&
|
||||||
!(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS);
|
!(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import PublishReleaseDate from './view';
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
releaseTime: makeSelectPublishFormValue('releaseTime')(state),
|
releaseTime: makeSelectPublishFormValue('releaseTime')(state),
|
||||||
releaseTimeEdited: makeSelectPublishFormValue('releaseTimeEdited')(state),
|
releaseTimeEdited: makeSelectPublishFormValue('releaseTimeEdited')(state),
|
||||||
|
releaseTimeError: makeSelectPublishFormValue('releaseTimeError')(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
|
|
@ -14,6 +14,7 @@ function dateToLinuxTimestamp(date: Date) {
|
||||||
const NOW = 'now';
|
const NOW = 'now';
|
||||||
const DEFAULT = 'default';
|
const DEFAULT = 'default';
|
||||||
const RESET_TO_ORIGINAL = 'reset-to-original';
|
const RESET_TO_ORIGINAL = 'reset-to-original';
|
||||||
|
const FUTURE_DATE_ERROR = 'Cannot set to a future date.';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
releaseTime: ?number,
|
releaseTime: ?number,
|
||||||
|
@ -35,6 +36,7 @@ const PublishReleaseDate = (props: Props) => {
|
||||||
} = props;
|
} = props;
|
||||||
const maxDate = useMaxDate ? new Date() : undefined;
|
const maxDate = useMaxDate ? new Date() : undefined;
|
||||||
const [date, setDate] = React.useState(releaseTime ? linuxTimestampToDate(releaseTime) : new Date());
|
const [date, setDate] = React.useState(releaseTime ? linuxTimestampToDate(releaseTime) : new Date());
|
||||||
|
const [error, setError] = React.useState([]);
|
||||||
|
|
||||||
const isNew = releaseTime === undefined;
|
const isNew = releaseTime === undefined;
|
||||||
const isEdit = !isNew || allowDefault === false;
|
const isEdit = !isNew || allowDefault === false;
|
||||||
|
@ -45,7 +47,38 @@ const PublishReleaseDate = (props: Props) => {
|
||||||
// const showDatePicker = isEdit || releaseTimeEdited !== undefined;
|
// const showDatePicker = isEdit || releaseTimeEdited !== undefined;
|
||||||
const showDatePicker = true;
|
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 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) {
|
if (value) {
|
||||||
newDate(value);
|
newDate(value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,6 +93,8 @@ const PublishReleaseDate = (props: Props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
function newDate(value: string | Date) {
|
function newDate(value: string | Date) {
|
||||||
|
updateError('clear', FUTURE_DATE_ERROR);
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case NOW:
|
case NOW:
|
||||||
const newDate = new Date();
|
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(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
updatePublishForm({ releaseTimeEdited: undefined });
|
updatePublishForm({ releaseTimeEdited: undefined });
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updatePublishForm({ releaseTimeError: error.join(';') });
|
||||||
|
}, [error]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-field-date-picker">
|
<div className="form-field-date-picker">
|
||||||
<label>{__('Release date')}</label>
|
<label>{__('Release date')}</label>
|
||||||
<div className="controls">
|
<div className="form-field-date-picker__controls">
|
||||||
{showDatePicker && (
|
{showDatePicker && (
|
||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
className="date-picker-input"
|
className="date-picker-input"
|
||||||
calendarClassName="form-field-calendar"
|
calendarClassName="form-field-calendar"
|
||||||
|
onBlur={handleBlur}
|
||||||
onChange={onDateTimePickerChanged}
|
onChange={onDateTimePickerChanged}
|
||||||
value={date}
|
value={date}
|
||||||
maxDate={maxDate}
|
|
||||||
format="y-MM-dd h:mm a"
|
format="y-MM-dd h:mm a"
|
||||||
disableClock
|
disableClock
|
||||||
clearIcon={null}
|
clearIcon={null}
|
||||||
|
@ -142,6 +192,12 @@ const PublishReleaseDate = (props: Props) => {
|
||||||
onClick={() => newDate(DEFAULT)}
|
onClick={() => newDate(DEFAULT)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{error.length > 0 && (
|
||||||
|
<span className="form-field-date-picker__error">
|
||||||
|
{error.includes(FUTURE_DATE_ERROR) && <span>{__(FUTURE_DATE_ERROR)}</span>}
|
||||||
|
{(!error.includes(FUTURE_DATE_ERROR) || error.length > 1) && <span>{__('Invalid date/time.')}</span>}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -78,6 +78,7 @@ const defaultState: PublishState = {
|
||||||
language: '',
|
language: '',
|
||||||
releaseTime: undefined,
|
releaseTime: undefined,
|
||||||
releaseTimeEdited: undefined,
|
releaseTimeEdited: undefined,
|
||||||
|
releaseTimeError: false,
|
||||||
releaseAnytime: false,
|
releaseAnytime: false,
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
channel: CHANNEL_ANONYMOUS,
|
channel: CHANNEL_ANONYMOUS,
|
||||||
|
|
|
@ -596,15 +596,6 @@ fieldset-section {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.date-picker-input,
|
|
||||||
.button--link {
|
|
||||||
margin-right: var(--spacing-m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-datetime-picker__button {
|
.react-datetime-picker__button {
|
||||||
svg {
|
svg {
|
||||||
stroke: var(--color-text);
|
stroke: var(--color-text);
|
||||||
|
@ -802,6 +793,32 @@ fieldset-section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-field-date-picker__header {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field-date-picker__error {
|
||||||
|
color: var(--color-error);
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-right: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field-date-picker__controls {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.date-picker-input,
|
||||||
|
.button--link {
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-field-calendar {
|
.form-field-calendar {
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
|
|
Loading…
Add table
Reference in a new issue