From c0c7c237aa2e6e26ccccd2886dcd3b2b24f0e265 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Tue, 16 Feb 2021 12:07:30 +1000 Subject: [PATCH] FIX: Clean up topic-timer-info CSS classes, add edit button, and improve UI (#12080) This PR adds an edit button to the topic timer info message which opens the modal. Also, I have cleaned up a few more places where we were referencing "topic status update" which is what these were called prior to being called topic timers. The category settings for auto-close topic hours has now also been modified to use the new relative-time-picker component. Finally, the relative-time-picker input step and min is dynamic based on mins/other intervals selected, see https://review.discourse.org/t/feature-relative-time-input-for-timers-and-bookmarks-and-promote-auto-close-after-last-post-timer-12063/19204/7?u=martin --- .../app/components/edit-category-settings.js | 12 ++++ .../app/components/edit-topic-timer-form.js | 2 +- .../app/components/relative-time-picker.js | 57 +++++++++++++++++- .../app/components/topic-timer-info.js | 59 ++++++++----------- .../app/controllers/edit-topic-timer.js | 21 ++++--- .../javascripts/discourse/app/routes/topic.js | 2 +- .../components/edit-category-settings.hbs | 8 ++- .../components/edit-topic-timer-form.hbs | 2 +- .../components/relative-time-picker.hbs | 2 +- .../components/topic-footer-buttons.hbs | 2 +- .../templates/components/topic-timer-info.hbs | 25 ++++++-- .../discourse/app/templates/topic.hbs | 7 ++- .../discourse/app/widgets/topic-admin-menu.js | 4 +- .../discourse/tests/acceptance/modal-test.js | 2 +- .../tests/acceptance/topic-edit-timer-test.js | 38 ++++++------ .../widgets/topic-admin-menu-test.js | 2 +- .../common/base/edit-category.scss | 4 ++ .../common/base/edit-topic-timer-modal.scss | 4 +- .../stylesheets/common/printer-friendly.scss | 2 +- app/assets/stylesheets/desktop/topic.scss | 9 ++- app/assets/stylesheets/mobile/topic.scss | 2 +- config/locales/client.en.yml | 3 +- 22 files changed, 179 insertions(+), 90 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/edit-category-settings.js b/app/assets/javascripts/discourse/app/components/edit-category-settings.js index f04ffa6721..e7d2914f1f 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-settings.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-settings.js @@ -5,6 +5,7 @@ import { SEARCH_PRIORITIES } from "discourse/lib/constants"; import { buildCategoryPanel } from "discourse/components/edit-category-panel"; import discourseComputed from "discourse-common/utils/decorators"; import { setting } from "discourse/lib/computed"; +import { action } from "@ember/object"; const categorySortCriteria = []; export function addCategorySortCriteria(criteria) { @@ -126,4 +127,15 @@ export default buildCategoryPanel("settings", { { name: I18n.t("category.sort_descending"), value: false }, ]; }, + + @discourseComputed + hiddenRelativeIntervals() { + return ["mins"]; + }, + + @action + onAutoCloseDurationChange(minutes) { + let hours = minutes / 60; + this.set("category.auto_close_hours", hours); + }, }); diff --git a/app/assets/javascripts/discourse/app/components/edit-topic-timer-form.js b/app/assets/javascripts/discourse/app/components/edit-topic-timer-form.js index 4c35310ab8..2e10a86a76 100644 --- a/app/assets/javascripts/discourse/app/components/edit-topic-timer-form.js +++ b/app/assets/javascripts/discourse/app/components/edit-topic-timer-form.js @@ -153,7 +153,7 @@ export default Component.extend({ "useDuration", "topicTimer.duration_minutes" ) - showTopicStatusInfo( + showTopicTimerInfo( statusType, isCustom, updateTime, diff --git a/app/assets/javascripts/discourse/app/components/relative-time-picker.js b/app/assets/javascripts/discourse/app/components/relative-time-picker.js index 554947f279..45e04d5071 100644 --- a/app/assets/javascripts/discourse/app/components/relative-time-picker.js +++ b/app/assets/javascripts/discourse/app/components/relative-time-picker.js @@ -8,11 +8,53 @@ export default Component.extend({ tagName: "", selectedInterval: "mins", durationMinutes: null, + durationHours: null, duration: null, + hiddenIntervals: null, @on("init") cloneDuration() { let mins = this.durationMinutes; + let hours = this.durationHours; + + if (hours && mins) { + throw new Error( + "relative-time needs initial duration in hours OR minutes, both are not supported" + ); + } + + if (hours) { + this._setInitialDurationFromHours(hours); + } else { + this._setInitialDurationFromMinutes(mins); + } + }, + + @on("init") + setHiddenIntervals() { + this.hiddenIntervals = this.hiddenIntervals || []; + }, + + _setInitialDurationFromHours(hours) { + if (hours >= 730) { + this.setProperties({ + duration: Math.floor(hours / 30 / 24), + selectedInterval: "months", + }); + } else if (hours >= 24) { + this.setProperties({ + duration: Math.floor(hours / 24), + selectedInterval: "days", + }); + } else { + this.setProperties({ + duration: hours, + selectedInterval: "hours", + }); + } + }, + + _setInitialDurationFromMinutes(mins) { if (mins >= 43800) { this.setProperties({ duration: Math.floor(mins / 30 / 60 / 24), @@ -36,6 +78,16 @@ export default Component.extend({ } }, + @discourseComputed("selectedInterval") + durationMin(selectedInterval) { + return selectedInterval === "mins" ? 1 : 0.1; + }, + + @discourseComputed("selectedInterval") + durationStep(selectedInterval) { + return selectedInterval === "mins" ? 1 : 0.05; + }, + @discourseComputed("duration") intervals(duration) { const count = duration ? parseFloat(duration) : 0; @@ -57,7 +109,7 @@ export default Component.extend({ id: "months", name: I18n.t("relative_time_picker.months", { count }), }, - ]; + ].filter((interval) => !this.hiddenIntervals.includes(interval.id)); }, @discourseComputed("selectedInterval", "duration") @@ -68,7 +120,8 @@ export default Component.extend({ switch (interval) { case "mins": - mins = duration; + // we round up here in case the user manually inputted a step < 1 + mins = Math.ceil(duration); break; case "hours": mins = duration * 60; diff --git a/app/assets/javascripts/discourse/app/components/topic-timer-info.js b/app/assets/javascripts/discourse/app/components/topic-timer-info.js index ea3e1dc1a0..0eb2edd748 100644 --- a/app/assets/javascripts/discourse/app/components/topic-timer-info.js +++ b/app/assets/javascripts/discourse/app/components/topic-timer-info.js @@ -3,28 +3,46 @@ import Category from "discourse/models/category"; import Component from "@ember/component"; import { DELETE_REPLIES_TYPE } from "discourse/controllers/edit-topic-timer"; import I18n from "I18n"; -import discourseComputed from "discourse-common/utils/decorators"; +import discourseComputed, { on } from "discourse-common/utils/decorators"; import { iconHTML } from "discourse-common/lib/icon-library"; import { isTesting } from "discourse-common/config/environment"; export default Component.extend({ - classNames: ["topic-status-info"], + classNames: ["topic-timer-info"], _delayedRerender: null, clockIcon: `${iconHTML("far-clock")}`.htmlSafe(), - trashCanIcon: `${iconHTML("trash-alt")}`.htmlSafe(), - trashCanTitle: I18n.t("post.controls.remove_timer"), + trashLabel: I18n.t("post.controls.remove_timer"), title: null, notice: null, showTopicTimer: null, + showTopicTimerModal: null, + removeTopicTimer: null, + + @on("didReceiveAttrs") + setupRenderer() { + this.renderTopicTimer(); + }, + + @on("willDestroyElement") + cancelDelayedRenderer() { + if (this._delayedRerender) { + cancel(this._delayedRerender); + } + }, @discourseComputed - canRemoveTimer() { + canModifyTimer() { return this.currentUser && this.currentUser.get("canManageTopic"); }, - @discourseComputed("canRemoveTimer", "removeTopicTimer") - showTrashCan(canRemoveTimer, removeTopicTimer) { - return canRemoveTimer && removeTopicTimer; + @discourseComputed("canModifyTimer", "removeTopicTimer") + showTrashCan(canModifyTimer, removeTopicTimer) { + return canModifyTimer && removeTopicTimer; + }, + + @discourseComputed("canModifyTimer", "showTopicTimerModal") + showEdit(canModifyTimer, showTopicTimerModal) { + return canModifyTimer && showTopicTimerModal; }, renderTopicTimer() { @@ -102,31 +120,6 @@ export default Component.extend({ } }, - didReceiveAttrs() { - this._super(...arguments); - this.renderTopicTimer(); - }, - - didInsertElement() { - this._super(...arguments); - - if (this.removeTopicTimer) { - $(this.element).on( - "click.topic-timer-remove", - "button", - this.removeTopicTimer - ); - } - }, - - willDestroyElement() { - $(this.element).off("click.topic-timer-remove", this.removeTopicTimer); - - if (this._delayedRerender) { - cancel(this._delayedRerender); - } - }, - _noticeKey() { let statusType = this.statusType; if (statusType === "silent_close") { diff --git a/app/assets/javascripts/discourse/app/controllers/edit-topic-timer.js b/app/assets/javascripts/discourse/app/controllers/edit-topic-timer.js index a60f541d4f..01c0286b13 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-topic-timer.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-topic-timer.js @@ -29,14 +29,16 @@ export default Controller.extend(ModalFunctionality, { closed ? "topic.temp_open.title" : "topic.auto_close.title" ), }, - { + ]; + + if (!closed) { + types.push({ id: CLOSE_AFTER_LAST_POST_STATUS_TYPE, - name: I18n.t( - closed - ? "topic.temp_open.title" - : "topic.auto_close_after_last_post.title" - ), - }, + name: I18n.t("topic.auto_close_after_last_post.title"), + }); + } + + types.push( { id: OPEN_STATUS_TYPE, name: I18n.t( @@ -50,8 +52,9 @@ export default Controller.extend(ModalFunctionality, { { id: BUMP_TYPE, name: I18n.t("topic.auto_bump.title"), - }, - ]; + } + ); + if (this.currentUser.get("staff")) { types.push( { diff --git a/app/assets/javascripts/discourse/app/routes/topic.js b/app/assets/javascripts/discourse/app/routes/topic.js index 06c1351a85..ae9974569f 100644 --- a/app/assets/javascripts/discourse/app/routes/topic.js +++ b/app/assets/javascripts/discourse/app/routes/topic.js @@ -105,7 +105,7 @@ const TopicRoute = DiscourseRoute.extend({ }); }, - showTopicStatusUpdate() { + showTopicTimerModal() { const model = this.modelFor("topic"); const topicTimer = model.get("topic_timer"); diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs index 33e59c3a3b..8a8512bbf3 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs @@ -114,7 +114,13 @@ - {{text-field value=category.auto_close_hours id="topic-auto-close" type="number"}} +
+ {{relative-time-picker + id="topic-auto-close" + durationHours=category.auto_close_hours + hiddenIntervals=hiddenRelativeIntervals + onChange=(action "onAutoCloseDurationChange")}} +