diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-silence-user.hbs b/app/assets/javascripts/admin/addon/templates/modal/admin-silence-user.hbs index 6c7ad17458..e6a31315b5 100644 --- a/app/assets/javascripts/admin/addon/templates/modal/admin-silence-user.hbs +++ b/app/assets/javascripts/admin/addon/templates/modal/admin-silence-user.hbs @@ -12,7 +12,6 @@ {{future-date-input class="silence-until" label="admin.user.silence_duration" - includeFarFuture=true clearable=false input=silenceUntil onChangeInput=(action (mut silenceUntil)) diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-suspend-user.hbs b/app/assets/javascripts/admin/addon/templates/modal/admin-suspend-user.hbs index 361f283666..44f4a47ec1 100644 --- a/app/assets/javascripts/admin/addon/templates/modal/admin-suspend-user.hbs +++ b/app/assets/javascripts/admin/addon/templates/modal/admin-suspend-user.hbs @@ -13,7 +13,6 @@ {{future-date-input class="suspend-until" label="admin.user.suspend_duration" - includeFarFuture=true clearable=false input=suspendUntil onChangeInput=(action (mut suspendUntil)) diff --git a/app/assets/javascripts/discourse/app/components/future-date-input.js b/app/assets/javascripts/discourse/app/components/future-date-input.js index 77d25a2628..6c325c758a 100644 --- a/app/assets/javascripts/discourse/app/components/future-date-input.js +++ b/app/assets/javascripts/discourse/app/components/future-date-input.js @@ -1,9 +1,16 @@ import Component from "@ember/component"; import { action } from "@ember/object"; import { and, empty, equal } from "@ember/object/computed"; -import buildTimeframes from "discourse/lib/timeframes-builder"; import I18n from "I18n"; import { FORMAT } from "select-kit/components/future-date-input-selector"; +import discourseComputed from "discourse-common/utils/decorators"; +import { + TIME_SHORTCUT_TYPES, + extendedDefaultTimeShortcuts, + formatTime, + hideDynamicTimeShortcuts, + timeShortcuts, +} from "discourse/lib/time-shortcut"; export default Component.extend({ selection: null, @@ -24,12 +31,12 @@ export default Component.extend({ if (this.input) { const dateTime = moment(this.input); - const closestTimeframe = this.findClosestTimeframe(dateTime); - if (closestTimeframe) { - this.set("selection", closestTimeframe.id); + const closestShortcut = this._findClosestShortcut(dateTime); + if (closestShortcut) { + this.set("selection", closestShortcut.id); } else { this.setProperties({ - selection: "custom", + selection: TIME_SHORTCUT_TYPES.CUSTOM, _date: dateTime.format("YYYY-MM-DD"), _time: dateTime.format("HH:mm"), }); @@ -45,6 +52,36 @@ export default Component.extend({ } }, + @discourseComputed("customShortcuts") + shortcuts(customShortcuts) { + let shortcuts; + if (customShortcuts && customShortcuts.length) { + shortcuts = customShortcuts; + } else { + shortcuts = extendedDefaultTimeShortcuts(this.userTimezone); + } + + const shortcutsFactory = timeShortcuts(this.userTimezone); + if (this.includeDateTime) { + shortcuts.push(shortcutsFactory.custom()); + } + if (this.includeNow) { + shortcuts.push(shortcutsFactory.now()); + } + + shortcuts = hideDynamicTimeShortcuts(shortcuts, this.userTimezone); + + return shortcuts.map((s) => { + return { + id: s.id, + name: I18n.t(s.label), + time: s.time, + timeFormatted: formatTime(s), + icon: s.icon, + }; + }); + }, + @action onChangeDate(date) { if (!date) { @@ -73,15 +110,8 @@ export default Component.extend({ } }, - findClosestTimeframe(dateTime) { - const options = { - includeWeekend: this.includeWeekend, - includeFarFuture: this.includeFarFuture, - includeDateTime: this.includeDateTime, - canScheduleNow: this.includeNow || false, - }; - - return buildTimeframes(this.userTimezone, options).find((tf) => { + _findClosestShortcut(dateTime) { + return this.shortcuts.find((tf) => { if (tf.time) { const diff = tf.time.diff(dateTime); return 0 <= diff && diff < 60 * 1000; diff --git a/app/assets/javascripts/discourse/app/components/time-shortcut-picker.js b/app/assets/javascripts/discourse/app/components/time-shortcut-picker.js index 46890b6afe..c4b3e67e7a 100644 --- a/app/assets/javascripts/discourse/app/components/time-shortcut-picker.js +++ b/app/assets/javascripts/discourse/app/components/time-shortcut-picker.js @@ -1,7 +1,4 @@ import { - LATER_TODAY_CUTOFF_HOUR, - MOMENT_FRIDAY, - MOMENT_THURSDAY, START_OF_DAY_HOUR, laterToday, now, @@ -10,6 +7,8 @@ import { import { TIME_SHORTCUT_TYPES, defaultTimeShortcuts, + formatTime, + hideDynamicTimeShortcuts, specialShortcutOptions, } from "discourse/lib/time-shortcut"; import discourseComputed, { @@ -183,7 +182,7 @@ export default Component.extend({ } else { options = defaultTimeShortcuts(userTimezone); } - this._hideDynamicOptions(options); + options = hideDynamicTimeShortcuts(options, userTimezone); let specialOptions = specialShortcutOptions(); if (this.lastCustomDate && this.lastCustomTime) { @@ -206,7 +205,7 @@ export default Component.extend({ } this._applyCustomLabels(options, customLabels); - this._formatTime(options); + options.forEach((o) => (o.timeFormatted = formatTime(o))); return options; }, @@ -271,23 +270,4 @@ export default Component.extend({ } }); }, - - _hideDynamicOptions(options) { - if (now(this.userTimezone).hour() >= LATER_TODAY_CUTOFF_HOUR) { - this._hideOption(options, TIME_SHORTCUT_TYPES.LATER_TODAY); - } - - if (now(this.userTimezone).day() >= MOMENT_THURSDAY) { - this._hideOption(options, TIME_SHORTCUT_TYPES.LATER_THIS_WEEK); - } - - if (now(this.userTimezone).day() >= MOMENT_FRIDAY) { - this._hideOption(options, TIME_SHORTCUT_TYPES.THIS_WEEKEND); - } - }, - - _hideOption(options, optionId) { - const option = options.findBy("id", optionId); - option.hidden = true; - }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/create-invite.js b/app/assets/javascripts/discourse/app/controllers/create-invite.js index ad8a302ec1..b549e6826a 100644 --- a/app/assets/javascripts/discourse/app/controllers/create-invite.js +++ b/app/assets/javascripts/discourse/app/controllers/create-invite.js @@ -12,6 +12,7 @@ import Invite from "discourse/models/invite"; import I18n from "I18n"; import { FORMAT } from "select-kit/components/future-date-input-selector"; import { sanitize } from "discourse/lib/text"; +import { timeShortcuts } from "discourse/lib/time-shortcut"; export default Controller.extend( ModalFunctionality, @@ -177,6 +178,24 @@ export default Controller.extend( return staff || groups.any((g) => g.owner); }, + @discourseComputed + timeShortcuts() { + const timezone = this.currentUser.resolvedTimezone(this.currentUser); + const shortcuts = timeShortcuts(timezone); + return [ + shortcuts.laterToday(), + shortcuts.tomorrow(), + shortcuts.laterThisWeek(), + shortcuts.monday(), + shortcuts.twoWeeks(), + shortcuts.nextMonth(), + shortcuts.twoMonths(), + shortcuts.threeMonths(), + shortcuts.fourMonths(), + shortcuts.sixMonths(), + ]; + }, + @action copied() { this.save({ sendEmail: false, copy: true }); diff --git a/app/assets/javascripts/discourse/app/controllers/edit-slow-mode.js b/app/assets/javascripts/discourse/app/controllers/edit-slow-mode.js index 254f65a1a7..d666c40d7a 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-slow-mode.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-slow-mode.js @@ -7,6 +7,7 @@ import { action } from "@ember/object"; import discourseComputed from "discourse-common/utils/decorators"; import { equal, or } from "@ember/object/computed"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { timeShortcuts } from "discourse/lib/time-shortcut"; export default Controller.extend(ModalFunctionality, { selectedSlowMode: null, @@ -107,6 +108,24 @@ export default Controller.extend(ModalFunctionality, { : "topic.slow_mode_update.enable"; }, + @discourseComputed + timeShortcuts() { + const timezone = this.currentUser.resolvedTimezone(this.currentUser); + const shortcuts = timeShortcuts(timezone); + return [ + shortcuts.laterToday(), + shortcuts.tomorrow(), + shortcuts.laterThisWeek(), + shortcuts.monday(), + shortcuts.twoWeeks(), + shortcuts.nextMonth(), + shortcuts.twoMonths(), + shortcuts.threeMonths(), + shortcuts.fourMonths(), + shortcuts.sixMonths(), + ]; + }, + _setFromSeconds(seconds) { this.setProperties(fromSeconds(seconds)); }, diff --git a/app/assets/javascripts/discourse/app/controllers/ignore-duration-with-username.js b/app/assets/javascripts/discourse/app/controllers/ignore-duration-with-username.js index 7e6c9cf2e6..2b1e997a07 100644 --- a/app/assets/javascripts/discourse/app/controllers/ignore-duration-with-username.js +++ b/app/assets/javascripts/discourse/app/controllers/ignore-duration-with-username.js @@ -3,11 +3,35 @@ import I18n from "I18n"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import User from "discourse/models/user"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { timeShortcuts } from "discourse/lib/time-shortcut"; +import discourseComputed from "discourse-common/utils/decorators"; export default Controller.extend(ModalFunctionality, { loading: false, ignoredUntil: null, ignoredUsername: null, + + @discourseComputed + timeShortcuts() { + const timezone = this.currentUser.resolvedTimezone(this.currentUser); + const shortcuts = timeShortcuts(timezone); + return [ + shortcuts.laterToday(), + shortcuts.tomorrow(), + shortcuts.laterThisWeek(), + shortcuts.thisWeekend(), + shortcuts.monday(), + shortcuts.twoWeeks(), + shortcuts.nextMonth(), + shortcuts.twoMonths(), + shortcuts.threeMonths(), + shortcuts.fourMonths(), + shortcuts.sixMonths(), + shortcuts.oneYear(), + shortcuts.forever(), + ]; + }, + actions: { ignore() { if (!this.ignoredUntil || !this.ignoredUsername) { diff --git a/app/assets/javascripts/discourse/app/controllers/ignore-duration.js b/app/assets/javascripts/discourse/app/controllers/ignore-duration.js index 77e77f3e95..0ee1ae3fcb 100644 --- a/app/assets/javascripts/discourse/app/controllers/ignore-duration.js +++ b/app/assets/javascripts/discourse/app/controllers/ignore-duration.js @@ -2,10 +2,34 @@ import Controller from "@ember/controller"; import I18n from "I18n"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { timeShortcuts } from "discourse/lib/time-shortcut"; +import discourseComputed from "discourse-common/utils/decorators"; export default Controller.extend(ModalFunctionality, { loading: false, ignoredUntil: null, + + @discourseComputed + timeShortcuts() { + const timezone = this.currentUser.resolvedTimezone(this.currentUser); + const shortcuts = timeShortcuts(timezone); + return [ + shortcuts.laterToday(), + shortcuts.tomorrow(), + shortcuts.laterThisWeek(), + shortcuts.thisWeekend(), + shortcuts.monday(), + shortcuts.twoWeeks(), + shortcuts.nextMonth(), + shortcuts.twoMonths(), + shortcuts.threeMonths(), + shortcuts.fourMonths(), + shortcuts.sixMonths(), + shortcuts.oneYear(), + shortcuts.forever(), + ]; + }, + actions: { ignore() { if (!this.ignoredUntil) { diff --git a/app/assets/javascripts/discourse/app/lib/time-shortcut.js b/app/assets/javascripts/discourse/app/lib/time-shortcut.js index 219ed0d1ec..6d5751dc58 100644 --- a/app/assets/javascripts/discourse/app/lib/time-shortcut.js +++ b/app/assets/javascripts/discourse/app/lib/time-shortcut.js @@ -1,6 +1,10 @@ import { + LATER_TODAY_CUTOFF_HOUR, + MOMENT_FRIDAY, MOMENT_MONDAY, + MOMENT_SATURDAY, MOMENT_SUNDAY, + MOMENT_THURSDAY, fourMonths, laterThisWeek, laterToday, @@ -16,6 +20,7 @@ import { twoMonths, twoWeeks, } from "discourse/lib/time-utils"; +import I18n from "I18n"; export const TIME_SHORTCUT_TYPES = { LATER_TODAY: "later_today", @@ -46,6 +51,24 @@ export function defaultTimeShortcuts(timezone) { ]; } +export function extendedDefaultTimeShortcuts(timezone) { + const shortcuts = timeShortcuts(timezone); + return [ + shortcuts.laterToday(), + shortcuts.tomorrow(), + shortcuts.laterThisWeek(), + shortcuts.monday(), + shortcuts.twoWeeks(), + shortcuts.nextMonth(), + shortcuts.twoMonths(), + shortcuts.threeMonths(), + shortcuts.fourMonths(), + shortcuts.sixMonths(), + shortcuts.oneYear(), + shortcuts.forever(), + ]; +} + export function specialShortcutOptions() { const shortcuts = timeShortcuts(); return [shortcuts.lastCustom(), shortcuts.custom(), shortcuts.none()]; @@ -210,3 +233,33 @@ export function timeShortcuts(timezone) { }, }; } + +export function hideDynamicTimeShortcuts(shortcuts, timezone) { + const shortcutsToHide = new Set(); + const _now = now(timezone); + if (_now.hour() >= LATER_TODAY_CUTOFF_HOUR) { + shortcutsToHide.add(TIME_SHORTCUT_TYPES.LATER_TODAY); + } + + if (_now.day === MOMENT_SUNDAY || _now.day() >= MOMENT_THURSDAY) { + shortcutsToHide.add(TIME_SHORTCUT_TYPES.LATER_THIS_WEEK); + } + + if ( + _now.day() === MOMENT_FRIDAY || + _now.day() === MOMENT_SATURDAY || + _now.day() === MOMENT_SUNDAY + ) { + shortcutsToHide.add(TIME_SHORTCUT_TYPES.THIS_WEEKEND); + } + + return shortcuts.filter((s) => !shortcutsToHide.has(s.id)); +} + +export function formatTime(shortcut) { + if (!shortcut.time || !shortcut.timeFormatKey) { + return null; + } + + return shortcut.time.format(I18n.t(shortcut.timeFormatKey)); +} diff --git a/app/assets/javascripts/discourse/app/lib/time-utils.js b/app/assets/javascripts/discourse/app/lib/time-utils.js index e53a95e0e9..07063410f3 100644 --- a/app/assets/javascripts/discourse/app/lib/time-utils.js +++ b/app/assets/javascripts/discourse/app/lib/time-utils.js @@ -5,6 +5,8 @@ export const LATER_TODAY_CUTOFF_HOUR = 17; export const LATER_TODAY_MAX_HOUR = 18; export const MOMENT_SUNDAY = 0; export const MOMENT_MONDAY = 1; +export const MOMENT_TUESDAY = 2; +export const MOMENT_WEDNESDAY = 3; export const MOMENT_THURSDAY = 4; export const MOMENT_FRIDAY = 5; export const MOMENT_SATURDAY = 6; diff --git a/app/assets/javascripts/discourse/app/lib/timeframes-builder.js b/app/assets/javascripts/discourse/app/lib/timeframes-builder.js deleted file mode 100644 index e400bea624..0000000000 --- a/app/assets/javascripts/discourse/app/lib/timeframes-builder.js +++ /dev/null @@ -1,89 +0,0 @@ -import { - TIME_SHORTCUT_TYPES, - timeShortcuts, -} from "discourse/lib/time-shortcut"; -import I18n from "I18n"; - -export default function buildTimeframes(timezone, options = {}) { - const timeframes = allTimeframes(timezone); - formatTime(timeframes); - processDynamicTimeframes(timeframes, options, timezone); - return timeframes.filter((t) => !t.hidden); -} - -function allTimeframes(timezone) { - const shortcuts = timeShortcuts(timezone); - - return [ - shortcuts.now(), - shortcuts.laterToday(), - shortcuts.tomorrow(), - shortcuts.laterThisWeek(), - shortcuts.thisWeekend(), - shortcuts.monday(), - shortcuts.twoWeeks(), - shortcuts.nextMonth(), - shortcuts.twoMonths(), - shortcuts.threeMonths(), - shortcuts.fourMonths(), - shortcuts.sixMonths(), - shortcuts.oneYear(), - shortcuts.forever(), - shortcuts.custom(), - ]; -} - -function processDynamicTimeframes(timeframes, options, timezone) { - const now = moment.tz(timezone); - - if ( - !options.includeWeekend || - now.day() === 0 || - now.day() === 5 || - now.day() === 6 - ) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.THIS_WEEKEND); - } - - if (now.day() === 0) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.START_OF_NEXT_BUSINESS_WEEK); - } - - if (now.date() === moment.tz(timezone).endOf("month").date()) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.NEXT_MONTH); - } - - if (24 - now.hour() <= 6) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.LATER_TODAY); - } - - if (now.day() === 0 || now.day() >= 4) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.LATER_THIS_WEEK); - } - - if (!options.includeFarFuture) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.ONE_YEAR); - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.FOREVER); - } - - if (!options.includeDateTime) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.CUSTOM); - } - - if (!options.canScheduleNow) { - hideTimeframe(timeframes, TIME_SHORTCUT_TYPES.NOW); - } -} - -function hideTimeframe(timeframes, timeframeId) { - const timeframe = timeframes.findBy("id", timeframeId); - timeframe.hidden = true; -} - -function formatTime(options) { - options.forEach((option) => { - if (option.time && option.timeFormatKey) { - option.timeFormatted = option.time.format(I18n.t(option.timeFormatKey)); - } - }); -} diff --git a/app/assets/javascripts/discourse/app/templates/components/future-date-input.hbs b/app/assets/javascripts/discourse/app/templates/components/future-date-input.hbs index 3550669f31..9833dd995d 100644 --- a/app/assets/javascripts/discourse/app/templates/components/future-date-input.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/future-date-input.hbs @@ -4,12 +4,8 @@ {{#if displayLabelIcon}}{{d-icon displayLabelIcon}}{{/if}}{{displayLabel}} {{future-date-input-selector - statusType=statusType value=(readonly selection) - includeDateTime=includeDateTime - includeWeekend=includeWeekend - includeFarFuture=includeFarFuture - includeNow=includeNow + content=shortcuts clearable=clearable onChangeInput=onChangeInput onChange=(action (mut selection)) diff --git a/app/assets/javascripts/discourse/app/templates/components/invite-link-panel.hbs b/app/assets/javascripts/discourse/app/templates/components/invite-link-panel.hbs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/javascripts/discourse/app/templates/modal/create-invite.hbs b/app/assets/javascripts/discourse/app/templates/modal/create-invite.hbs index f0813af428..eee3bd1233 100644 --- a/app/assets/javascripts/discourse/app/templates/modal/create-invite.hbs +++ b/app/assets/javascripts/discourse/app/templates/modal/create-invite.hbs @@ -124,8 +124,7 @@ {{future-date-input displayLabelIcon="far-clock" displayLabel=(i18n "user.invited.invite.expires_at") - statusType="close" - includeDateTime=true + customShortcuts=timeShortcuts clearable=true input=buffered.expires_at onChangeInput=(action (mut buffered.expires_at)) diff --git a/app/assets/javascripts/discourse/app/templates/modal/edit-slow-mode.hbs b/app/assets/javascripts/discourse/app/templates/modal/edit-slow-mode.hbs index 824dd2fec1..aca17261fd 100644 --- a/app/assets/javascripts/discourse/app/templates/modal/edit-slow-mode.hbs +++ b/app/assets/javascripts/discourse/app/templates/modal/edit-slow-mode.hbs @@ -31,7 +31,7 @@ class="enabled-until" label="topic.slow_mode_update.enabled_until" labelClasses="slow-mode-label" - includeFarFuture=false + customShortcuts=timeShortcuts clearable=true input=model.slow_mode_enabled_until onChangeInput=(action (mut model.slow_mode_enabled_until)) diff --git a/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs b/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs index dc280e179d..351c3136c9 100644 --- a/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs +++ b/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs @@ -43,7 +43,6 @@
{{future-date-input class="pin-until" - includeFarFuture=true clearable=true input=model.pinnedInCategoryUntil onChangeInput=(action (mut model.pinnedInCategoryUntil)) @@ -57,7 +56,6 @@ {{d-icon "far-clock"}} {{future-date-input class="pin-until" - includeFarFuture=true clearable=true input=model.pinnedInCategoryUntil onChangeInput=(action (mut model.pinnedInCategoryUntil)) @@ -93,7 +91,6 @@
{{future-date-input class="pin-until" - includeFarFuture=true clearable=true input=model.pinnedGloballyUntil onChangeInput=(action (mut model.pinnedGloballyUntil)) @@ -107,7 +104,6 @@ {{d-icon "far-clock"}} {{future-date-input class="pin-until" - includeFarFuture=true clearable=true input=model.pinnedGloballyUntil onChangeInput=(action (mut model.pinnedGloballyUntil)) diff --git a/app/assets/javascripts/discourse/app/templates/modal/ignore-duration-with-username.hbs b/app/assets/javascripts/discourse/app/templates/modal/ignore-duration-with-username.hbs index 2cebb1831a..170e9530a7 100644 --- a/app/assets/javascripts/discourse/app/templates/modal/ignore-duration-with-username.hbs +++ b/app/assets/javascripts/discourse/app/templates/modal/ignore-duration-with-username.hbs @@ -13,9 +13,8 @@ {{future-date-input label="user.user_notifications.ignore_duration_when" input=(readonly ignoredUntil) - includeWeekend=true + customShortcuts=timeShortcuts includeDateTime=false - includeFarFuture=true onChangeInput=(action (mut ignoredUntil)) }}
{{i18n "user.user_notifications.ignore_duration_note"}}
diff --git a/app/assets/javascripts/discourse/app/templates/modal/ignore-duration.hbs b/app/assets/javascripts/discourse/app/templates/modal/ignore-duration.hbs index 566b60f21a..9825c0b511 100644 --- a/app/assets/javascripts/discourse/app/templates/modal/ignore-duration.hbs +++ b/app/assets/javascripts/discourse/app/templates/modal/ignore-duration.hbs @@ -2,9 +2,8 @@ {{future-date-input label="user.user_notifications.ignore_duration_when" input=ignoredUntil - includeWeekend=true + customShortcuts=timeShortcuts includeDateTime=false - includeFarFuture=true onChangeInput=(action (mut ignoredUntil)) }}{{i18n "user.user_notifications.ignore_duration_note"}}
diff --git a/app/assets/javascripts/discourse/tests/integration/components/future-date-input-selector-test.js b/app/assets/javascripts/discourse/tests/integration/components/future-date-input-selector-test.js deleted file mode 100644 index 6fba9aa951..0000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/future-date-input-selector-test.js +++ /dev/null @@ -1,82 +0,0 @@ -import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest, { - setupRenderingTest, -} from "discourse/tests/helpers/component-test"; -import { - discourseModule, - exists, - queryAll, -} from "discourse/tests/helpers/qunit-helpers"; -import hbs from "htmlbars-inline-precompile"; -import I18n from "I18n"; - -discourseModule( - "Unit | Lib | select-kit/future-date-input-selector", - function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function () { - this.set("subject", selectKit()); - }); - - hooks.afterEach(function () { - if (this.clock) { - this.clock.restore(); - } - }); - - componentTest("rendering and expanding", { - template: hbs` - {{future-date-input-selector - options=(hash - none="time_shortcut.select_timeframe" - ) - }} - `, - - async test(assert) { - assert.ok( - exists(".future-date-input-selector"), - "Selector is rendered" - ); - - assert.ok( - this.subject.header().label() === - I18n.t("time_shortcut.select_timeframe"), - "Default text is rendered" - ); - - await this.subject.expand(); - - assert.ok( - exists(".select-kit-collection"), - "List of options is rendered" - ); - }, - }); - - componentTest("shows 'Custom date and time' if it's enabled", { - template: hbs` - {{future-date-input-selector - includeDateTime=true - }} - `, - - async test(assert) { - await this.subject.expand(); - const options = getOptions(); - const customDateAndTime = I18n.t("time_shortcut.custom"); - - assert.ok(options.includes(customDateAndTime)); - }, - }); - - function getOptions() { - return Array.from( - queryAll(`.select-kit-collection .select-kit-row`).map( - (_, span) => span.dataset.name - ) - ); - } - } -); diff --git a/app/assets/javascripts/discourse/tests/integration/components/future-date-input-test.js b/app/assets/javascripts/discourse/tests/integration/components/future-date-input-test.js new file mode 100644 index 0000000000..d7e2cdc1e0 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/integration/components/future-date-input-test.js @@ -0,0 +1,136 @@ +import selectKit from "discourse/tests/helpers/select-kit-helper"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import { + discourseModule, + exists, + fakeTime, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import hbs from "htmlbars-inline-precompile"; +import I18n from "I18n"; + +discourseModule("Unit | Lib | select-kit/future-date-input", function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); + + hooks.afterEach(function () { + if (this.clock) { + this.clock.restore(); + } + }); + + componentTest("rendering and expanding", { + template: hbs` + {{future-date-input + options=(hash + none="time_shortcut.select_timeframe" + ) + }} + `, + + async test(assert) { + assert.ok(exists(".future-date-input-selector"), "Selector is rendered"); + + assert.ok( + this.subject.header().label() === + I18n.t("time_shortcut.select_timeframe"), + "Default text is rendered" + ); + + await this.subject.expand(); + + assert.ok( + exists(".select-kit-collection"), + "List of options is rendered" + ); + }, + }); + + componentTest("renders default options", { + template: hbs`{{future-date-input}}`, + + beforeEach() { + const monday = "2100-12-13T08:00:00"; + this.clock = fakeTime(monday, this.currentUser._timezone, true); + }, + + async test(assert) { + await this.subject.expand(); + const options = getOptions(); + const expected = [ + I18n.t("time_shortcut.later_today"), + I18n.t("time_shortcut.tomorrow"), + I18n.t("time_shortcut.later_this_week"), + I18n.t("time_shortcut.start_of_next_business_week_alt"), + I18n.t("time_shortcut.two_weeks"), + I18n.t("time_shortcut.next_month"), + I18n.t("time_shortcut.two_months"), + I18n.t("time_shortcut.three_months"), + I18n.t("time_shortcut.four_months"), + I18n.t("time_shortcut.six_months"), + I18n.t("time_shortcut.one_year"), + I18n.t("time_shortcut.forever"), + I18n.t("time_shortcut.custom"), + ]; + + assert.deepEqual(options, expected); + }, + }); + + componentTest("shows 'Custom date and time' by default", { + template: hbs`{{future-date-input}}`, + + async test(assert) { + await this.subject.expand(); + const options = getOptions(); + const customDateAndTime = I18n.t("time_shortcut.custom"); + + assert.ok(options.includes(customDateAndTime)); + }, + }); + + componentTest("doesn't show 'Custom date and time' if disabled", { + template: hbs` + {{future-date-input + includeDateTime=false + }} + `, + + async test(assert) { + await this.subject.expand(); + const options = getOptions(); + const customDateAndTime = I18n.t("time_shortcut.custom"); + + assert.notOk(options.includes(customDateAndTime)); + }, + }); + + componentTest("shows the now option if enabled", { + template: hbs` + {{future-date-input + includeNow=true + }} + `, + + async test(assert) { + await this.subject.expand(); + const options = getOptions(); + const now = I18n.t("time_shortcut.now"); + + assert.ok(options.includes(now)); + }, + }); + + function getOptions() { + return Array.from( + queryAll(`.select-kit-collection .select-kit-row`).map( + (_, span) => span.dataset.name + ) + ); + } +}); diff --git a/app/assets/javascripts/discourse/tests/unit/lib/time-shortcut-test.js b/app/assets/javascripts/discourse/tests/unit/lib/time-shortcut-test.js new file mode 100644 index 0000000000..1d8b5e1ff0 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/unit/lib/time-shortcut-test.js @@ -0,0 +1,92 @@ +import { module, test } from "qunit"; +import { fakeTime } from "discourse/tests/helpers/qunit-helpers"; +import { + defaultTimeShortcuts, + hideDynamicTimeShortcuts, +} from "discourse/lib/time-shortcut"; + +module( + "Unit | Lib | time-shortcut | hideDynamicTimeShortcuts", + function (hooks) { + hooks.afterEach(function () { + if (this.clock) { + this.clock.restore(); + } + }); + + test("hides 'Later Today' at the end of the day", function (assert) { + const timezone = moment.tz.guess(); + const shortcuts = defaultTimeShortcuts(timezone); + + this.clock = fakeTime("2100-04-19 08:00:00", timezone, true); // morning + let result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.ok( + result.includes("later_today"), + "shows later_today in the morning" + ); + + this.clock = fakeTime("2100-04-19 18:00:00", timezone, true); // evening + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk(result.includes("doesn't show later_today in the evening")); + }); + + test("hides 'Later This Week' starting from Thursday", function (assert) { + const timezone = moment.tz.guess(); + const shortcuts = defaultTimeShortcuts(timezone); + + this.clock = fakeTime("2100-04-21 18:00:00", timezone, true); // Wednesday + let result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.ok( + result.includes("later_this_week"), + "shows later_this_week on Wednesdays" + ); + + this.clock = fakeTime("2100-04-22 18:00:00", timezone, true); // Thursday + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk( + result.includes("later_this_week"), + "doesn't show later_this_week on Thursdays" + ); + + this.clock = fakeTime("2100-04-23 18:00:00", timezone, true); // Friday + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk( + result.includes("later_this_week"), + "doesn't show later_this_week on Fridays" + ); + }); + + test("hides 'This Weekend' on Fridays, Saturdays and Sundays", function (assert) { + const timezone = moment.tz.guess(); + const shortcuts = defaultTimeShortcuts(timezone); + + this.clock = fakeTime("2100-04-22 18:00:00", timezone, true); // Thursday + let result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.ok( + result.includes("this_weekend"), + "shows this_weekend on Thursdays" + ); + + this.clock = fakeTime("2100-04-23 18:00:00", timezone, true); // Friday + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk( + result.includes("this_weekend"), + "doesn't show this_weekend on Fridays" + ); + + this.clock = fakeTime("2100-04-24 18:00:00", timezone, true); // Saturday + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk( + result.includes("this_weekend"), + "doesn't show this_weekend on Saturdays" + ); + + this.clock = fakeTime("2100-04-25 18:00:00", timezone, true); // Sunday + result = hideDynamicTimeShortcuts(shortcuts, timezone).mapBy("id"); + assert.notOk( + result.includes("this_weekend"), + "doesn't show this_weekend on Sundays" + ); + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/unit/lib/timeframes-builder-test.js b/app/assets/javascripts/discourse/tests/unit/lib/timeframes-builder-test.js deleted file mode 100644 index 51dd461af5..0000000000 --- a/app/assets/javascripts/discourse/tests/unit/lib/timeframes-builder-test.js +++ /dev/null @@ -1,169 +0,0 @@ -import { module, test } from "qunit"; -import { fakeTime } from "discourse/tests/helpers/qunit-helpers"; -import buildTimeframes from "discourse/lib/timeframes-builder"; - -module("Unit | Lib | timeframes-builder", function (hooks) { - hooks.afterEach(function () { - if (this.clock) { - this.clock.restore(); - } - }); - - test("default options", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-07T08:00:00", timezone, true); // Monday - - const expected = [ - "later_today", - "tomorrow", - "later_this_week", - "start_of_next_business_week", - "two_weeks", - "next_month", - "two_months", - "three_months", - "four_months", - "six_months", - ]; - - assert.deepEqual(buildTimeframes(timezone).mapBy("id"), expected); - }); - - test("doesn't output 'Next Week' on Sundays", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-13T08:00:00", timezone, true); // Sunday - - assert.ok( - !buildTimeframes(timezone) - .mapBy("id") - .includes("start_of_next_business_week") - ); - }); - - test("outputs 'This Weekend' if it's enabled", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-07T08:00:00", timezone, true); // Monday - - assert.ok( - buildTimeframes(timezone, { includeWeekend: true }) - .mapBy("id") - .includes("this_weekend") - ); - }); - - test("doesn't output 'This Weekend' on Fridays", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-23 18:00:00", timezone, true); // Friday - - assert.ok( - !buildTimeframes(timezone, { includeWeekend: true }) - .mapBy("id") - .includes("this_weekend") - ); - }); - - test("doesn't show 'This Weekend' on Sundays", function (assert) { - /* - We need this test to avoid regressions. - We tend to write such conditions and think that - they mean the beginning of work week - (Monday, Tuesday and Wednesday in this specific case): - - if (date.day <= 3) { - ... - } - - In fact, Sunday will pass this check too, because - in moment.js 0 stands for Sunday. - */ - - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-25 18:00:00", timezone, true); // Sunday - - assert.ok( - !buildTimeframes(timezone, { includeWeekend: true }) - .mapBy("id") - .includes("this_weekend") - ); - }); - - test("outputs 'Later This Week' instead of 'Later Today' at the end of the day", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-19 18:00:00", timezone, true); // Monday evening - const timeframes = buildTimeframes(timezone).mapBy("id"); - - assert.notOk(timeframes.includes("later_today")); - assert.ok(timeframes.includes("later_this_week")); - }); - - test("doesn't output 'Later This Week' on Thursdays", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-22 18:00:00", timezone, true); // Thursday evening - const timeframes = buildTimeframes(timezone).mapBy("id"); - - assert.notOk(timeframes.includes("later_this_week")); - }); - - test("doesn't output 'Later This Week' on Sundays", function (assert) { - /* - We need this test to avoid regressions. - We tend to write such conditions and think that - they mean the beginning of business week - (Monday, Tuesday and Wednesday in this specific case): - - if (date.day < 3) { - ... - } - - In fact, Sunday will pass this check too, because - in moment.js 0 stands for Sunday. - */ - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-25 18:00:00", timezone, true); // Sunday evening - const timeframes = buildTimeframes(timezone).mapBy("id"); - - assert.notOk(timeframes.includes("later_this_week")); - }); - - test("doesn't output 'Next Month' on the last day of the month", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-04-30 18:00:00", timezone, true); // The last day of April - const timeframes = buildTimeframes(timezone).mapBy("id"); - - assert.notOk(timeframes.includes("next_month")); - }); - - test("shows far future options if enabled", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-07T08:00:00", timezone, true); // Monday - - const timeframes = buildTimeframes(timezone, { - includeFarFuture: true, - }).mapBy("id"); - - assert.ok(timeframes.includes("one_year")); - assert.ok(timeframes.includes("forever")); - }); - - test("shows the pick-date-and-time option if enabled", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-07T08:00:00", timezone, true); // Monday - - const timeframes = buildTimeframes(timezone, { - includeDateTime: true, - }).mapBy("id"); - - assert.ok(timeframes.includes("custom")); - }); - - test("shows the now option if enabled", function (assert) { - const timezone = moment.tz.guess(); - this.clock = fakeTime("2100-06-07T08:00:00", timezone, true); // Monday - - const timeframes = buildTimeframes(timezone, { - canScheduleNow: true, - }).mapBy("id"); - - assert.ok(timeframes.includes("now")); - }); -}); diff --git a/app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js index 53b845905f..18031e21f7 100644 --- a/app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js +++ b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js @@ -1,9 +1,6 @@ import ComboBoxComponent from "select-kit/components/combo-box"; -import { computed } from "@ember/object"; import { equal } from "@ember/object/computed"; import { isEmpty } from "@ember/utils"; -import buildTimeframes from "discourse/lib/timeframes-builder"; -import I18n from "I18n"; export const FORMAT = "YYYY-MM-DD HH:mmZ"; @@ -28,25 +25,6 @@ export default ComboBoxComponent.extend({ return "future-date-input-selector/future-date-input-selector-row"; }, - content: computed("statusType", function () { - const opts = { - includeWeekend: this.includeWeekend, - includeFarFuture: this.includeFarFuture, - includeDateTime: this.includeDateTime, - canScheduleNow: this.includeNow || false, - }; - - return buildTimeframes(this.userTimezone, opts).map((tf) => { - return { - id: tf.id, - name: I18n.t(tf.label), - time: tf.time, - timeFormatted: tf.timeFormatted, - icon: tf.icon, - }; - }); - }), - actions: { onChange(value) { if (value !== "custom" && !isEmpty(value)) { diff --git a/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/date-time-inputs.hbs b/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/date-time-inputs.hbs index 51cbad64f6..c55c80fbfb 100644 --- a/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/date-time-inputs.hbs +++ b/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/date-time-inputs.hbs @@ -21,7 +21,6 @@ {{#styleguide-example title="future-date-input"}} {{future-date-input displayLabelIcon="far-clock" - includeDateTime=true clearable=true }} {{/styleguide-example}} diff --git a/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/dropdowns.hbs b/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/dropdowns.hbs index 81226a37dc..0426135687 100644 --- a/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/dropdowns.hbs +++ b/plugins/styleguide/assets/javascripts/discourse/templates/styleguide/atoms/dropdowns.hbs @@ -58,7 +58,6 @@ {{#styleguide-example title="future-date-input-selector"}} {{future-date-input-selector - statusType="open" input=dummy.topicTimerUpdateDate includeWeekend=true includeForever=true