diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index ef43c4444a688..eb1f3c4db0d61 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -306,6 +306,7 @@ def article_params_json %i[ title body_markdown main_image published description video_thumbnail_url tag_list canonical_url series collection_id archived published_at timezone + published_at_date published_at_time ] end @@ -319,10 +320,14 @@ def article_params_json end time_zone_str = params["article"].delete("timezone") - if params["article"]["published_at"] + + time = params["article"].delete("published_at_time") + date = params["article"].delete("published_at_date") + + if date.present? time_zone = Time.find_zone(time_zone_str) time_zone ||= Time.find_zone("UTC") - params["article"]["published_at"] = time_zone.parse(params["article"]["published_at"]) + params["article"]["published_at"] = time_zone.parse("#{date} #{time}") end @article_params_json = params.require(:article).permit(allowed_params) diff --git a/app/javascript/article-form/articleForm.jsx b/app/javascript/article-form/articleForm.jsx index 9838e3793d2fa..bd96913b82fd5 100644 --- a/app/javascript/article-form/articleForm.jsx +++ b/app/javascript/article-form/articleForm.jsx @@ -2,6 +2,7 @@ import { h, Component } from 'preact'; import PropTypes from 'prop-types'; import linkState from 'linkstate'; import postscribe from 'postscribe'; +import moment from 'moment'; import { KeyboardShortcuts } from '../shared/components/useKeyboardShortcuts'; import { embedGists } from '../utilities/gist'; import { submitArticle, previewArticle } from './actions'; @@ -18,7 +19,7 @@ import { getOSKeyboardModifierKeyString } from '@utilities/runtime'; /* global activateRunkitTags */ /* - Although the state fields: id, description, canonicalUrl, publishedAt, series, allSeries and + Although the state fields: id, description, canonicalUrl, publishedAtDate, publishedAtTime, series, allSeries and editing are not used in this file, they are important to the editor. */ @@ -96,6 +97,14 @@ export class ArticleForm extends Component { } : {}; + this.publishedAtTime, (this.publishedAtDate = ''); + + if (this.article.published_at) { + const publishedAt = moment(this.article.published_at); + this.publishedAtTime = publishedAt.format('HH:mm'); + this.publishedAtDate = publishedAt.format('YYYY-MM-DD'); + } + this.state = { formKey: new Date().toISOString(), id: this.article.id || null, // eslint-disable-line react/no-unused-state @@ -103,7 +112,8 @@ export class ArticleForm extends Component { tagList: this.article.cached_tag_list || '', description: '', // eslint-disable-line react/no-unused-state canonicalUrl: this.article.canonical_url || '', // eslint-disable-line react/no-unused-state - publishedAt: this.article.published_at || '', // eslint-disable-line react/no-unused-state + publishedAtTime: this.publishedAtTime, + publishedAtDate: this.publishedAtDate, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || '', // eslint-disable-line react/no-unused-state series: this.article.series || '', // eslint-disable-line react/no-unused-state allSeries: this.article.all_series || [], // eslint-disable-line react/no-unused-state @@ -335,7 +345,8 @@ export class ArticleForm extends Component { tagList: this.article.cached_tag_list || '', description: '', // eslint-disable-line react/no-unused-state canonicalUrl: this.article.canonical_url || '', // eslint-disable-line react/no-unused-state - publishedAt: this.article.published_at || '', // eslint-disable-line react/no-unused-state + publishedAtTime: this.publishedAtTime, + publishedAtDate: this.publishedAtDate, series: this.article.series || '', // eslint-disable-line react/no-unused-state allSeries: this.article.all_series || [], // eslint-disable-line react/no-unused-state bodyMarkdown: this.article.body_markdown || '', @@ -397,7 +408,8 @@ export class ArticleForm extends Component { tagList, bodyMarkdown, published, - publishedAt, + publishedAtTime, + publishedAtDate, previewShowing, previewLoading, previewResponse, @@ -498,7 +510,8 @@ export class ArticleForm extends Component { now; + const now = moment(); + const publishedAtObj = publishedAtDate + ? moment(`${publishedAtDate} ${publishedAtTime || '00:00'}`) + : now; + const schedule = publishedAtObj > now; const saveButtonText = schedule ? 'Schedule' @@ -95,7 +99,8 @@ EditorActions.propTypes = { onSaveDraft: PropTypes.func.isRequired, onPublish: PropTypes.func.isRequired, published: PropTypes.bool.isRequired, - publishedAt: PropTypes.string.isRequired, + publishedAtTime: PropTypes.string.isRequired, + publishedAtDate: PropTypes.string.isRequired, schedulingEnabled: PropTypes.bool.isRequired, edited: PropTypes.bool.isRequired, version: PropTypes.string.isRequired, diff --git a/app/javascript/article-form/components/Options.jsx b/app/javascript/article-form/components/Options.jsx index 58ac3b19acb6f..c36c570d02b8e 100644 --- a/app/javascript/article-form/components/Options.jsx +++ b/app/javascript/article-form/components/Options.jsx @@ -1,5 +1,6 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; +import moment from 'moment'; import { Dropdown, ButtonNew as Button } from '@crayons'; import CogIcon from '@images/cog.svg'; @@ -12,24 +13,11 @@ import CogIcon from '@images/cog.svg'; * @param {Function} props.onConfigChange Callback for when the config options have changed */ -function toISOStringLocal(datetime) { - const month = (datetime.getMonth() + 1).toString().padStart(2, '0'); - const day = datetime.getDate().toString().padStart(2, '0'); - return [ - datetime.getFullYear(), - '-', - month, - '-', - day, - 'T', - datetime.toLocaleTimeString(), - ].join(''); -} - export const Options = ({ passedData: { published = false, - publishedAt = '', + publishedAtDate = '', + publishedAtTime = '', timezone = Intl.DateTimeFormat().resolvedOptions().timeZone, allSeries = [], canonicalUrl = '', @@ -43,13 +31,9 @@ export const Options = ({ let publishedField = ''; let existingSeries = ''; let publishedAtField = ''; - const publishedDate = new Date(publishedAt); - const currentDate = new Date(); - - const localPublishedAt = toISOStringLocal(publishedDate); - const minPublishedAt = toISOStringLocal(currentDate); - const readonlyPublishedAt = published && publishedDate < currentDate; + const readonlyPublishedAt = + published && moment(`${publishedAtDate} ${publishedAtTime}`) < moment(); if (allSeries.length > 0) { const seriesNames = allSeries.map((name, index) => { @@ -92,20 +76,33 @@ export const Options = ({ ); } + if (schedulingEnabled && !readonlyPublishedAt) { + const currentDate = moment().format('YYYY-MM-DD'); publishedAtField = (
-