mirror of
https://github.com/discourse/discourse.git
synced 2025-03-15 18:45:35 +08:00
DEV: Convert feature-topic
modal to component-based API (#23277)
Updated styles with some sign off from @jordanvidrine <img width="655" alt="Screenshot 2023-08-28 at 9 07 47 AM" src="https://github.com/discourse/discourse/assets/50783505/31b453ef-a787-436f-9fd9-48c9cd3a2e81">
This commit is contained in:
parent
81d8c6ba6c
commit
6870f38e87
app/assets/javascripts/discourse/app
components/modal
controllers
routes
services
templates/modal
@ -0,0 +1,232 @@
|
|||||||
|
<DModal
|
||||||
|
class="feature-topic"
|
||||||
|
@title={{i18n "topic.feature_topic.title"}}
|
||||||
|
@closeModal={{@closeModal}}
|
||||||
|
>
|
||||||
|
<:body>
|
||||||
|
{{#if @model.topic.pinned_at}}
|
||||||
|
<div class="feature-section">
|
||||||
|
<div class="desc">
|
||||||
|
{{#if @model.topic.pinned_globally}}
|
||||||
|
<p>
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@size="small"
|
||||||
|
@condition={{this.loading}}
|
||||||
|
>
|
||||||
|
{{#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}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
</p>
|
||||||
|
<p>{{i18n "topic.feature_topic.global_pin_note"}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p>
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@size="small"
|
||||||
|
@condition={{this.loading}}
|
||||||
|
>
|
||||||
|
{{html-safe this.alreadyPinnedMessage}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
</p>
|
||||||
|
<p>{{i18n "topic.feature_topic.pin_note"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
<p>{{html-safe this.unPinMessage}}</p>
|
||||||
|
<p><DButton
|
||||||
|
@action={{this.unpin}}
|
||||||
|
@icon="thumbtack"
|
||||||
|
@label="topic.feature.unpin"
|
||||||
|
class="btn-primary"
|
||||||
|
/></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="feature-section">
|
||||||
|
<div class="desc">
|
||||||
|
<p>
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@size="small"
|
||||||
|
@condition={{this.loading}}
|
||||||
|
>
|
||||||
|
{{html-safe this.alreadyPinnedMessage}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{i18n "topic.feature_topic.pin_note"}}
|
||||||
|
</p>
|
||||||
|
{{#if this.site.isMobileDevice}}
|
||||||
|
<p>
|
||||||
|
{{html-safe this.pinMessage}}
|
||||||
|
</p>
|
||||||
|
<p class="with-validation">
|
||||||
|
<FutureDateInput
|
||||||
|
class="pin-until"
|
||||||
|
@clearable={{true}}
|
||||||
|
@input={{@model.topic.pinnedInCategoryUntil}}
|
||||||
|
@onChangeInput={{action
|
||||||
|
(mut @model.topic.pinnedInCategoryUntil)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PopupInputTip
|
||||||
|
@validation={{this.pinInCategoryValidation}}
|
||||||
|
@shownAt={{this.pinInCategoryTipShownAt}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="with-validation">
|
||||||
|
{{html-safe this.pinMessage}}
|
||||||
|
<span>
|
||||||
|
{{d-icon "far-clock"}}
|
||||||
|
<FutureDateInput
|
||||||
|
class="pin-until"
|
||||||
|
@clearable={{true}}
|
||||||
|
@input={{@model.topic.pinnedInCategoryUntil}}
|
||||||
|
@onChangeInput={{action
|
||||||
|
(mut @model.topic.pinnedInCategoryUntil)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PopupInputTip
|
||||||
|
@validation={{this.pinInCategoryValidation}}
|
||||||
|
@shownAt={{this.pinInCategoryTipShownAt}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
<p>
|
||||||
|
<DButton
|
||||||
|
@action={{this.pin}}
|
||||||
|
@icon="thumbtack"
|
||||||
|
@label="topic.feature.pin"
|
||||||
|
class="btn-primary"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if this.canPinGlobally}}
|
||||||
|
<hr />
|
||||||
|
<div class="feature-section">
|
||||||
|
<div class="desc">
|
||||||
|
<p>
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@size="small"
|
||||||
|
@condition={{this.loading}}
|
||||||
|
>
|
||||||
|
{{#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}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{i18n "topic.feature_topic.global_pin_note"}}
|
||||||
|
</p>
|
||||||
|
{{#if this.site.isMobileDevice}}
|
||||||
|
<p>
|
||||||
|
{{i18n "topic.feature_topic.pin_globally"}}
|
||||||
|
</p>
|
||||||
|
<p class="with-validation">
|
||||||
|
<FutureDateInput
|
||||||
|
class="pin-until"
|
||||||
|
@clearable={{true}}
|
||||||
|
@input={{@model.topic.pinnedGloballyUntil}}
|
||||||
|
@onChangeInput={{action
|
||||||
|
(mut @model.topic.pinnedGloballyUntil)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PopupInputTip
|
||||||
|
@validation={{this.pinGloballyValidation}}
|
||||||
|
@shownAt={{this.pinGloballyTipShownAt}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="with-validation">
|
||||||
|
{{i18n "topic.feature_topic.pin_globally"}}
|
||||||
|
<span>
|
||||||
|
{{d-icon "far-clock"}}
|
||||||
|
<FutureDateInput
|
||||||
|
class="pin-until"
|
||||||
|
@clearable={{true}}
|
||||||
|
@input={{@model.topic.pinnedGloballyUntil}}
|
||||||
|
@onChangeInput={{action
|
||||||
|
(mut @model.topic.pinnedGloballyUntil)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PopupInputTip
|
||||||
|
@validation={{this.pinGloballyValidation}}
|
||||||
|
@shownAt={{this.pinGloballyTipShownAt}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
<p>
|
||||||
|
<DButton
|
||||||
|
@action={{this.pinGlobally}}
|
||||||
|
@icon="thumbtack"
|
||||||
|
@label="topic.feature.pin_globally"
|
||||||
|
class="btn-primary"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
<hr />
|
||||||
|
{{#if this.currentUser.staff}}
|
||||||
|
<div class="feature-section">
|
||||||
|
<div class="desc">
|
||||||
|
<p>
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@size="small"
|
||||||
|
@condition={{this.loading}}
|
||||||
|
>
|
||||||
|
{{#if this.bannerCount}}
|
||||||
|
{{html-safe (i18n "topic.feature_topic.banner_exists")}}
|
||||||
|
{{else}}
|
||||||
|
{{html-safe (i18n "topic.feature_topic.no_banner_exists")}}
|
||||||
|
{{/if}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{i18n "topic.feature_topic.banner_note"}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{#if @model.topic.isBanner}}
|
||||||
|
{{i18n "topic.feature_topic.remove_banner"}}
|
||||||
|
{{else}}
|
||||||
|
{{i18n "topic.feature_topic.make_banner"}}
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{#if @model.topic.isBanner}}
|
||||||
|
<DButton
|
||||||
|
@action={{this.removeBanner}}
|
||||||
|
@icon="thumbtack"
|
||||||
|
@label="topic.feature.remove_banner"
|
||||||
|
class="btn-primary"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@action={{this.makeBanner}}
|
||||||
|
@icon="thumbtack"
|
||||||
|
@label="topic.feature.make_banner"
|
||||||
|
class="btn-primary make-banner"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</:body>
|
||||||
|
</DModal>
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
@ -15,6 +15,7 @@ import PublishPageModal from "discourse/components/modal/publish-page";
|
|||||||
import EditSlowModeModal from "discourse/components/modal/edit-slow-mode";
|
import EditSlowModeModal from "discourse/components/modal/edit-slow-mode";
|
||||||
import ChangeTimestampModal from "discourse/components/modal/change-timestamp";
|
import ChangeTimestampModal from "discourse/components/modal/change-timestamp";
|
||||||
import EditTopicTimerModal from "discourse/components/modal/edit-topic-timer";
|
import EditTopicTimerModal from "discourse/components/modal/edit-topic-timer";
|
||||||
|
import FeatureTopicModal from "discourse/components/modal/feature-topic";
|
||||||
|
|
||||||
const SCROLL_DELAY = 500;
|
const SCROLL_DELAY = 500;
|
||||||
|
|
||||||
@ -154,11 +155,22 @@ const TopicRoute = DiscourseRoute.extend({
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
showFeatureTopic() {
|
showFeatureTopic() {
|
||||||
showModal("feature-topic", {
|
const topicController = this.controllerFor("topic");
|
||||||
model: this.modelFor("topic"),
|
const model = this.modelFor("topic");
|
||||||
title: "topic.feature_topic.title",
|
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
|
@action
|
||||||
|
@ -18,7 +18,6 @@ const KNOWN_LEGACY_MODALS = [
|
|||||||
"create-account",
|
"create-account",
|
||||||
"create-invite-bulk",
|
"create-invite-bulk",
|
||||||
"create-invite",
|
"create-invite",
|
||||||
"feature-topic",
|
|
||||||
"flag",
|
"flag",
|
||||||
"grant-badge",
|
"grant-badge",
|
||||||
"group-default-notifications",
|
"group-default-notifications",
|
||||||
|
@ -1,213 +0,0 @@
|
|||||||
<DModalBody @class="feature-topic">
|
|
||||||
{{#if this.model.pinned_at}}
|
|
||||||
<div class="feature-section">
|
|
||||||
<div class="desc">
|
|
||||||
{{#if this.model.pinned_globally}}
|
|
||||||
<p>
|
|
||||||
<ConditionalLoadingSpinner
|
|
||||||
@size="small"
|
|
||||||
@condition={{this.loading}}
|
|
||||||
>
|
|
||||||
{{#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}}
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
</p>
|
|
||||||
<p>{{i18n "topic.feature_topic.global_pin_note"}}</p>
|
|
||||||
{{else}}
|
|
||||||
<p>
|
|
||||||
<ConditionalLoadingSpinner
|
|
||||||
@size="small"
|
|
||||||
@condition={{this.loading}}
|
|
||||||
>
|
|
||||||
{{html-safe this.alreadyPinnedMessage}}
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
</p>
|
|
||||||
<p>{{i18n "topic.feature_topic.pin_note"}}</p>
|
|
||||||
{{/if}}
|
|
||||||
<p>{{html-safe this.unPinMessage}}</p>
|
|
||||||
<p><DButton
|
|
||||||
@action={{action "unpin"}}
|
|
||||||
@icon="thumbtack"
|
|
||||||
@label="topic.feature.unpin"
|
|
||||||
@class="btn-primary"
|
|
||||||
/></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="feature-section">
|
|
||||||
<div class="desc">
|
|
||||||
<p>
|
|
||||||
<ConditionalLoadingSpinner @size="small" @condition={{this.loading}}>
|
|
||||||
{{html-safe this.alreadyPinnedMessage}}
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{i18n "topic.feature_topic.pin_note"}}
|
|
||||||
</p>
|
|
||||||
{{#if this.site.isMobileDevice}}
|
|
||||||
<p>
|
|
||||||
{{html-safe this.pinMessage}}
|
|
||||||
</p>
|
|
||||||
<p class="with-validation">
|
|
||||||
<FutureDateInput
|
|
||||||
@class="pin-until"
|
|
||||||
@clearable={{true}}
|
|
||||||
@input={{this.model.pinnedInCategoryUntil}}
|
|
||||||
@onChangeInput={{action (mut this.model.pinnedInCategoryUntil)}}
|
|
||||||
/>
|
|
||||||
<PopupInputTip
|
|
||||||
@validation={{this.pinInCategoryValidation}}
|
|
||||||
@shownAt={{this.pinInCategoryTipShownAt}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<p class="with-validation">
|
|
||||||
{{html-safe this.pinMessage}}
|
|
||||||
<span>
|
|
||||||
{{d-icon "far-clock"}}
|
|
||||||
<FutureDateInput
|
|
||||||
@class="pin-until"
|
|
||||||
@clearable={{true}}
|
|
||||||
@input={{this.model.pinnedInCategoryUntil}}
|
|
||||||
@onChangeInput={{action (mut this.model.pinnedInCategoryUntil)}}
|
|
||||||
/>
|
|
||||||
<PopupInputTip
|
|
||||||
@validation={{this.pinInCategoryValidation}}
|
|
||||||
@shownAt={{this.pinInCategoryTipShownAt}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
<p>
|
|
||||||
<DButton
|
|
||||||
@action={{action "pin"}}
|
|
||||||
@icon="thumbtack"
|
|
||||||
@label="topic.feature.pin"
|
|
||||||
@class="btn-primary"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{#if this.canPinGlobally}}
|
|
||||||
<div class="feature-section">
|
|
||||||
<div class="desc">
|
|
||||||
<p>
|
|
||||||
<ConditionalLoadingSpinner
|
|
||||||
@size="small"
|
|
||||||
@condition={{this.loading}}
|
|
||||||
>
|
|
||||||
{{#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}}
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{i18n "topic.feature_topic.global_pin_note"}}
|
|
||||||
</p>
|
|
||||||
{{#if this.site.isMobileDevice}}
|
|
||||||
<p>
|
|
||||||
{{i18n "topic.feature_topic.pin_globally"}}
|
|
||||||
</p>
|
|
||||||
<p class="with-validation">
|
|
||||||
<FutureDateInput
|
|
||||||
@class="pin-until"
|
|
||||||
@clearable={{true}}
|
|
||||||
@input={{this.model.pinnedGloballyUntil}}
|
|
||||||
@onChangeInput={{action (mut this.model.pinnedGloballyUntil)}}
|
|
||||||
/>
|
|
||||||
<PopupInputTip
|
|
||||||
@validation={{this.pinGloballyValidation}}
|
|
||||||
@shownAt={{this.pinGloballyTipShownAt}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<p class="with-validation">
|
|
||||||
{{i18n "topic.feature_topic.pin_globally"}}
|
|
||||||
<span>
|
|
||||||
{{d-icon "far-clock"}}
|
|
||||||
<FutureDateInput
|
|
||||||
@class="pin-until"
|
|
||||||
@clearable={{true}}
|
|
||||||
@input={{this.model.pinnedGloballyUntil}}
|
|
||||||
@onChangeInput={{action (mut this.model.pinnedGloballyUntil)}}
|
|
||||||
/>
|
|
||||||
<PopupInputTip
|
|
||||||
@validation={{this.pinGloballyValidation}}
|
|
||||||
@shownAt={{this.pinGloballyTipShownAt}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
<p>
|
|
||||||
<DButton
|
|
||||||
@action={{action "pinGlobally"}}
|
|
||||||
@icon="thumbtack"
|
|
||||||
@label="topic.feature.pin_globally"
|
|
||||||
@class="btn-primary"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if this.currentUser.staff}}
|
|
||||||
<div class="feature-section">
|
|
||||||
<div class="desc">
|
|
||||||
<p>
|
|
||||||
<ConditionalLoadingSpinner @size="small" @condition={{this.loading}}>
|
|
||||||
{{#if this.bannerCount}}
|
|
||||||
{{html-safe (i18n "topic.feature_topic.banner_exists")}}
|
|
||||||
{{else}}
|
|
||||||
{{html-safe (i18n "topic.feature_topic.no_banner_exists")}}
|
|
||||||
{{/if}}
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{i18n "topic.feature_topic.banner_note"}}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{#if this.model.isBanner}}
|
|
||||||
{{i18n "topic.feature_topic.remove_banner"}}
|
|
||||||
{{else}}
|
|
||||||
{{i18n "topic.feature_topic.make_banner"}}
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{#if this.model.isBanner}}
|
|
||||||
<DButton
|
|
||||||
@action={{action "removeBanner"}}
|
|
||||||
@icon="thumbtack"
|
|
||||||
@label="topic.feature.remove_banner"
|
|
||||||
@class="btn-primary"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@action={{action "makeBanner"}}
|
|
||||||
@icon="thumbtack"
|
|
||||||
@label="topic.feature.make_banner"
|
|
||||||
@class="btn-primary make-banner"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</DModalBody>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DModalCancel @close={{route-action "closeModal"}} />
|
|
||||||
</div>
|
|
Loading…
x
Reference in New Issue
Block a user