From 6870f38e87c3a8ec447ca8c8ec65992b560d0279 Mon Sep 17 00:00:00 2001 From: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com> Date: Mon, 28 Aug 2023 09:41:14 -0500 Subject: [PATCH] DEV: Convert `feature-topic` modal to component-based API (#23277) Updated styles with some sign off from @jordanvidrine Screenshot 2023-08-28 at 9 07 47 AM --- .../app/components/modal/feature-topic.hbs | 232 ++++++++++++++++++ .../app/components/modal/feature-topic.js | 180 ++++++++++++++ .../app/controllers/feature-topic.js | 183 -------------- .../javascripts/discourse/app/routes/topic.js | 20 +- .../discourse/app/services/modal.js | 1 - .../app/templates/modal/feature-topic.hbs | 213 ---------------- 6 files changed, 428 insertions(+), 401 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/modal/feature-topic.hbs create mode 100644 app/assets/javascripts/discourse/app/components/modal/feature-topic.js delete mode 100644 app/assets/javascripts/discourse/app/controllers/feature-topic.js delete mode 100644 app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs diff --git a/app/assets/javascripts/discourse/app/components/modal/feature-topic.hbs b/app/assets/javascripts/discourse/app/components/modal/feature-topic.hbs new file mode 100644 index 00000000000..3d97a250c46 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/feature-topic.hbs @@ -0,0 +1,232 @@ + + <:body> + {{#if @model.topic.pinned_at}} +
+
+ {{#if @model.topic.pinned_globally}} +

+ + {{#if this.pinnedGloballyCount}} + {{html-safe + (i18n + "topic.feature_topic.already_pinned_globally" + count=this.pinnedGloballyCount + ) + }} + {{else}} + {{html-safe (i18n "topic.feature_topic.not_pinned_globally")}} + {{/if}} + +

+

{{i18n "topic.feature_topic.global_pin_note"}}

+ {{else}} +

+ + {{html-safe this.alreadyPinnedMessage}} + +

+

{{i18n "topic.feature_topic.pin_note"}}

+ {{/if}} +

{{html-safe this.unPinMessage}}

+

+
+
+ {{else}} +
+
+

+ + {{html-safe this.alreadyPinnedMessage}} + +

+

+ {{i18n "topic.feature_topic.pin_note"}} +

+ {{#if this.site.isMobileDevice}} +

+ {{html-safe this.pinMessage}} +

+

+ + +

+ {{else}} +

+ {{html-safe this.pinMessage}} + + {{d-icon "far-clock"}} + + + +

+ {{/if}} +

+ +

+
+
+ {{#if this.canPinGlobally}} +
+
+
+

+ + {{#if this.pinnedGloballyCount}} + {{html-safe + (i18n + "topic.feature_topic.already_pinned_globally" + count=this.pinnedGloballyCount + ) + }} + {{else}} + {{html-safe (i18n "topic.feature_topic.not_pinned_globally")}} + {{/if}} + +

+

+ {{i18n "topic.feature_topic.global_pin_note"}} +

+ {{#if this.site.isMobileDevice}} +

+ {{i18n "topic.feature_topic.pin_globally"}} +

+

+ + +

+ {{else}} +

+ {{i18n "topic.feature_topic.pin_globally"}} + + {{d-icon "far-clock"}} + + + +

+ {{/if}} +

+ +

+
+
+ {{/if}} + {{/if}} +
+ {{#if this.currentUser.staff}} +
+
+

+ + {{#if this.bannerCount}} + {{html-safe (i18n "topic.feature_topic.banner_exists")}} + {{else}} + {{html-safe (i18n "topic.feature_topic.no_banner_exists")}} + {{/if}} + +

+

+ {{i18n "topic.feature_topic.banner_note"}} +

+

+ {{#if @model.topic.isBanner}} + {{i18n "topic.feature_topic.remove_banner"}} + {{else}} + {{i18n "topic.feature_topic.make_banner"}} + {{/if}} +

+

+ {{#if @model.topic.isBanner}} + + {{else}} + + {{/if}} +

+
+
+ {{/if}} + +
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/modal/feature-topic.js b/app/assets/javascripts/discourse/app/components/modal/feature-topic.js new file mode 100644 index 00000000000..b729f986e42 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/feature-topic.js @@ -0,0 +1,180 @@ +import Component from "@glimmer/component"; +import I18n from "I18n"; +import { inject as service } from "@ember/service"; +import { ajax } from "discourse/lib/ajax"; +import EmberObject, { action } from "@ember/object"; +import { categoryLinkHTML } from "discourse/helpers/category-link"; +import { tracked } from "@glimmer/tracking"; + +export default class FeatureTopic extends Component { + @service currentUser; + @service dialog; + + @tracked loading = true; + @tracked pinnedInCategoryCount = 0; + @tracked pinnedGloballyCount = 0; + @tracked bannerCount = 0; + @tracked pinInCategoryTipShownAt = false; + @tracked pinGloballyTipShownAt = false; + + constructor() { + super(...arguments); + this.loadFeatureStats(); + } + + get categoryLink() { + return categoryLinkHTML(this.args.model.topic.category, { + allowUncategorized: true, + }); + } + + get unPinMessage() { + let name = "topic.feature_topic.unpin"; + if (this.args.model.topic.pinned_globally) { + name += "_globally"; + } + if (moment(this.args.model.topic.pinned_until) > moment()) { + name += "_until"; + } + const until = moment(this.args.model.topic.pinned_until).format("LL"); + return I18n.t(name, { categoryLink: this.categoryLink, until }); + } + + get canPinGlobally() { + return ( + this.currentUser.canManageTopic && + this.args.model.topic.details.can_pin_unpin_topic + ); + } + + get pinMessage() { + return I18n.t("topic.feature_topic.pin", { + categoryLink: this.categoryLink, + }); + } + + get alreadyPinnedMessage() { + const key = + this.pinnedInCategoryCount === 0 + ? "topic.feature_topic.not_pinned" + : "topic.feature_topic.already_pinned"; + return I18n.t(key, { + categoryLink: this.categoryLink, + count: this.pinnedInCategoryCount, + }); + } + + get pinDisabled() { + return !this._isDateValid(this.parsedPinnedInCategoryUntil); + } + + get pinGloballyDisabled() { + return !this._isDateValid(this.parsedPinnedGloballyUntil); + } + + get parsedPinnedInCategoryUntil() { + return this._parseDate(this.args.model.topic.pinnedInCategoryUntil); + } + + get parsedPinnedGloballyUntil() { + return this._parseDate(this.args.model.topic.pinnedGloballyUntil); + } + + get pinInCategoryValidation() { + if (this.pinDisabled) { + return EmberObject.create({ + failed: true, + reason: I18n.t("topic.feature_topic.pin_validation"), + }); + } + } + + get pinGloballyValidation() { + if (this.pinGloballyDisabled) { + return EmberObject.create({ + failed: true, + reason: I18n.t("topic.feature_topic.pin_validation"), + }); + } + } + + _parseDate(date) { + return moment(date, ["YYYY-MM-DD", "YYYY-MM-DD HH:mm"]); + } + + _isDateValid(parsedDate) { + return parsedDate.isValid() && parsedDate > moment(); + } + + @action + async loadFeatureStats() { + try { + this.loading = true; + const result = await ajax("/topics/feature_stats.json", { + data: { category_id: this.args.model.topic.category.id }, + }); + + if (result) { + this.pinnedInCategoryCount = result.pinned_in_category_count; + this.pinnedGloballyCount = result.pinned_globally_count; + this.bannerCount = result.banner_count; + } + } finally { + this.loading = false; + } + } + + async _confirmBeforePinningGlobally() { + if (this.pinnedGloballyCount < 4) { + this.args.model.pinGlobally(); + this.args.closeModal(); + } else { + this.dialog.yesNoConfirm({ + message: I18n.t("topic.feature_topic.confirm_pin_globally", { + count: this.pinnedGloballyCount, + }), + didConfirm: () => { + this.args.model.pinGlobally(); + this.args.closeModal(); + }, + }); + } + } + + @action + pin() { + if (this.pinDisabled) { + this.pinInCategoryTipShownAt = Date.now(); + } else { + this.args.model.togglePinned(); + this.args.closeModal(); + } + } + + @action + pinGlobally() { + if (this.pinGloballyDisabled) { + this.pinGloballyTipShownAt = Date.now(); + } else { + this._confirmBeforePinningGlobally(); + } + } + + @action + unpin() { + this.args.model.togglePinned(); + this.args.closeModal(); + } + + @action + makeBanner() { + this.args.model.makeBanner(); + this.args.closeModal(); + } + + @action + removeBanner() { + this.args.model.removeBanner(); + this.args.closeModal(); + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/feature-topic.js b/app/assets/javascripts/discourse/app/controllers/feature-topic.js deleted file mode 100644 index d6ac71574d6..00000000000 --- a/app/assets/javascripts/discourse/app/controllers/feature-topic.js +++ /dev/null @@ -1,183 +0,0 @@ -import Controller, { inject as controller } from "@ember/controller"; -import EmberObject from "@ember/object"; -import I18n from "I18n"; -import ModalFunctionality from "discourse/mixins/modal-functionality"; -import { ajax } from "discourse/lib/ajax"; -import { categoryLinkHTML } from "discourse/helpers/category-link"; -import discourseComputed from "discourse-common/utils/decorators"; -import { inject as service } from "@ember/service"; - -export default Controller.extend(ModalFunctionality, { - topicController: controller("topic"), - dialog: service(), - - loading: true, - pinnedInCategoryCount: 0, - pinnedGloballyCount: 0, - bannerCount: 0, - - reset() { - this.setProperties({ - "model.pinnedInCategoryUntil": null, - "model.pinnedGloballyUntil": null, - pinInCategoryTipShownAt: false, - pinGloballyTipShownAt: false, - }); - }, - - @discourseComputed("model.category") - categoryLink(category) { - return categoryLinkHTML(category, { allowUncategorized: true }); - }, - - @discourseComputed( - "categoryLink", - "model.pinned_globally", - "model.pinned_until" - ) - unPinMessage(categoryLink, pinnedGlobally, pinnedUntil) { - let name = "topic.feature_topic.unpin"; - if (pinnedGlobally) { - name += "_globally"; - } - if (moment(pinnedUntil) > moment()) { - name += "_until"; - } - const until = moment(pinnedUntil).format("LL"); - - return I18n.t(name, { categoryLink, until }); - }, - - @discourseComputed("model.details.can_pin_unpin_topic") - canPinGlobally(canPinUnpinTopic) { - return this.currentUser.canManageTopic && canPinUnpinTopic; - }, - - @discourseComputed("categoryLink") - pinMessage(categoryLink) { - return I18n.t("topic.feature_topic.pin", { categoryLink }); - }, - - @discourseComputed("categoryLink", "pinnedInCategoryCount") - alreadyPinnedMessage(categoryLink, count) { - const key = - count === 0 - ? "topic.feature_topic.not_pinned" - : "topic.feature_topic.already_pinned"; - return I18n.t(key, { categoryLink, count }); - }, - - @discourseComputed("parsedPinnedInCategoryUntil") - pinDisabled(parsedPinnedInCategoryUntil) { - return !this._isDateValid(parsedPinnedInCategoryUntil); - }, - - @discourseComputed("parsedPinnedGloballyUntil") - pinGloballyDisabled(parsedPinnedGloballyUntil) { - return !this._isDateValid(parsedPinnedGloballyUntil); - }, - - @discourseComputed("model.pinnedInCategoryUntil") - parsedPinnedInCategoryUntil(pinnedInCategoryUntil) { - return this._parseDate(pinnedInCategoryUntil); - }, - - @discourseComputed("model.pinnedGloballyUntil") - parsedPinnedGloballyUntil(pinnedGloballyUntil) { - return this._parseDate(pinnedGloballyUntil); - }, - - @discourseComputed("pinDisabled") - pinInCategoryValidation(pinDisabled) { - if (pinDisabled) { - return EmberObject.create({ - failed: true, - reason: I18n.t("topic.feature_topic.pin_validation"), - }); - } - }, - - @discourseComputed("pinGloballyDisabled") - pinGloballyValidation(pinGloballyDisabled) { - if (pinGloballyDisabled) { - return EmberObject.create({ - failed: true, - reason: I18n.t("topic.feature_topic.pin_validation"), - }); - } - }, - - _parseDate(date) { - return moment(date, ["YYYY-MM-DD", "YYYY-MM-DD HH:mm"]); - }, - - _isDateValid(parsedDate) { - return parsedDate.isValid() && parsedDate > moment(); - }, - - onShow() { - this.set("loading", true); - - return ajax("/topics/feature_stats.json", { - data: { category_id: this.get("model.category.id") }, - }) - .then((result) => { - if (result) { - this.setProperties({ - pinnedInCategoryCount: result.pinned_in_category_count, - pinnedGloballyCount: result.pinned_globally_count, - bannerCount: result.banner_count, - }); - } - }) - .finally(() => this.set("loading", false)); - }, - - _forwardAction(name) { - this.topicController.send(name); - this.send("closeModal"); - }, - - _confirmBeforePinningGlobally() { - const count = this.pinnedGloballyCount; - - if (count < 4) { - this._forwardAction("pinGlobally"); - } else { - this.send("hideModal"); - this.dialog.yesNoConfirm({ - message: I18n.t("topic.feature_topic.confirm_pin_globally", { count }), - didConfirm: () => this._forwardAction("pinGlobally"), - didCancel: () => this.send("reopenModal"), - }); - } - }, - - actions: { - pin() { - if (this.pinDisabled) { - this.set("pinInCategoryTipShownAt", Date.now()); - } else { - this._forwardAction("togglePinned"); - } - }, - - pinGlobally() { - if (this.pinGloballyDisabled) { - this.set("pinGloballyTipShownAt", Date.now()); - } else { - this._confirmBeforePinningGlobally(); - } - }, - - unpin() { - this._forwardAction("togglePinned"); - }, - makeBanner() { - this._forwardAction("makeBanner"); - }, - removeBanner() { - this._forwardAction("removeBanner"); - }, - }, -}); diff --git a/app/assets/javascripts/discourse/app/routes/topic.js b/app/assets/javascripts/discourse/app/routes/topic.js index 1d6ec7f6dcb..499d1a50f9c 100644 --- a/app/assets/javascripts/discourse/app/routes/topic.js +++ b/app/assets/javascripts/discourse/app/routes/topic.js @@ -15,6 +15,7 @@ import PublishPageModal from "discourse/components/modal/publish-page"; import EditSlowModeModal from "discourse/components/modal/edit-slow-mode"; import ChangeTimestampModal from "discourse/components/modal/change-timestamp"; import EditTopicTimerModal from "discourse/components/modal/edit-topic-timer"; +import FeatureTopicModal from "discourse/components/modal/feature-topic"; const SCROLL_DELAY = 500; @@ -154,11 +155,22 @@ const TopicRoute = DiscourseRoute.extend({ @action showFeatureTopic() { - showModal("feature-topic", { - model: this.modelFor("topic"), - title: "topic.feature_topic.title", + const topicController = this.controllerFor("topic"); + const model = this.modelFor("topic"); + model.setProperties({ + pinnedInCategoryUntil: null, + pinnedGloballyUntil: null, + }); + + this.modal.show(FeatureTopicModal, { + model: { + topic: model, + pinGlobally: () => topicController.send("pinGlobally"), + togglePinned: () => topicController.send("togglePinned"), + makeBanner: () => topicController.send("makeBanner"), + removeBanner: () => topicController.send("removeBanner"), + }, }); - this.controllerFor("feature_topic").reset(); }, @action diff --git a/app/assets/javascripts/discourse/app/services/modal.js b/app/assets/javascripts/discourse/app/services/modal.js index 70661bfeb23..ad454f48b57 100644 --- a/app/assets/javascripts/discourse/app/services/modal.js +++ b/app/assets/javascripts/discourse/app/services/modal.js @@ -18,7 +18,6 @@ const KNOWN_LEGACY_MODALS = [ "create-account", "create-invite-bulk", "create-invite", - "feature-topic", "flag", "grant-badge", "group-default-notifications", diff --git a/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs b/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs deleted file mode 100644 index 186590276d9..00000000000 --- a/app/assets/javascripts/discourse/app/templates/modal/feature-topic.hbs +++ /dev/null @@ -1,213 +0,0 @@ - - {{#if this.model.pinned_at}} -
-
- {{#if this.model.pinned_globally}} -

- - {{#if this.pinnedGloballyCount}} - {{html-safe - (i18n - "topic.feature_topic.already_pinned_globally" - count=this.pinnedGloballyCount - ) - }} - {{else}} - {{html-safe (i18n "topic.feature_topic.not_pinned_globally")}} - {{/if}} - -

-

{{i18n "topic.feature_topic.global_pin_note"}}

- {{else}} -

- - {{html-safe this.alreadyPinnedMessage}} - -

-

{{i18n "topic.feature_topic.pin_note"}}

- {{/if}} -

{{html-safe this.unPinMessage}}

-

-
-
- {{else}} -
-
-

- - {{html-safe this.alreadyPinnedMessage}} - -

-

- {{i18n "topic.feature_topic.pin_note"}} -

- {{#if this.site.isMobileDevice}} -

- {{html-safe this.pinMessage}} -

-

- - -

- {{else}} -

- {{html-safe this.pinMessage}} - - {{d-icon "far-clock"}} - - - -

- {{/if}} -

- -

-
-
- {{#if this.canPinGlobally}} -
-
-

- - {{#if this.pinnedGloballyCount}} - {{html-safe - (i18n - "topic.feature_topic.already_pinned_globally" - count=this.pinnedGloballyCount - ) - }} - {{else}} - {{html-safe (i18n "topic.feature_topic.not_pinned_globally")}} - {{/if}} - -

-

- {{i18n "topic.feature_topic.global_pin_note"}} -

- {{#if this.site.isMobileDevice}} -

- {{i18n "topic.feature_topic.pin_globally"}} -

-

- - -

- {{else}} -

- {{i18n "topic.feature_topic.pin_globally"}} - - {{d-icon "far-clock"}} - - - -

- {{/if}} -

- -

-
-
- {{/if}} - {{/if}} - {{#if this.currentUser.staff}} -
-
-

- - {{#if this.bannerCount}} - {{html-safe (i18n "topic.feature_topic.banner_exists")}} - {{else}} - {{html-safe (i18n "topic.feature_topic.no_banner_exists")}} - {{/if}} - -

-

- {{i18n "topic.feature_topic.banner_note"}} -

-

- {{#if this.model.isBanner}} - {{i18n "topic.feature_topic.remove_banner"}} - {{else}} - {{i18n "topic.feature_topic.make_banner"}} - {{/if}} -

-

- {{#if this.model.isBanner}} - - {{else}} - - {{/if}} -

-
-
- {{/if}} -
- \ No newline at end of file