diff --git a/app/assets/javascripts/discourse/app/components/choose-topic.hbs b/app/assets/javascripts/discourse/app/components/choose-topic.hbs index 15e42850396..9f9e243cd51 100644 --- a/app/assets/javascripts/discourse/app/components/choose-topic.hbs +++ b/app/assets/javascripts/discourse/app/components/choose-topic.hbs @@ -9,6 +9,7 @@ @value={{this.topicTitle}} @placeholderKey="choose_topic.title.placeholder" @id="choose-topic-title" + {{did-insert this.focusInput}} /> {{#if this.loading}} diff --git a/app/assets/javascripts/discourse/app/components/choose-topic.js b/app/assets/javascripts/discourse/app/components/choose-topic.js index d064d51761f..335f2eaf8ca 100644 --- a/app/assets/javascripts/discourse/app/components/choose-topic.js +++ b/app/assets/javascripts/discourse/app/components/choose-topic.js @@ -140,6 +140,13 @@ export default Component.extend({ } }, + @action + focusInput(element) { + if (this.autoFocus) { + element.focus(); + } + }, + _handleEnter(event) { if (event.key === "Enter") { event.preventDefault(); diff --git a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs new file mode 100644 index 00000000000..b08bc15fb22 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.hbs @@ -0,0 +1,225 @@ + + <:body> + {{#if @model.topic.isPrivateMessage}} +
+ {{#if this.canSplitToPM}} + + {{/if}} + + +
+ + {{#if this.canSplitTopic}} + {{#if this.newMessage}} +

+ {{html-safe + (i18n + "topic.move_to_new_message.instructions" + count=@model.selectedPostsCount + ) + }} +

+
+ + + + {{#if this.canTagMessages}} + + + {{/if}} + + {{/if}} + {{/if}} + + {{#if this.existingMessage}} +

+ {{html-safe + (i18n + "topic.move_to_existing_message.instructions" + count=@model.selectedPostsCount + ) + }} +

+
+ + + + + + {{#if this.selectedTopicId}} +
+ + {{/if}} + + {{/if}} + + {{else}} + +
+ {{#if this.canSplitTopic}} + + {{/if}} + + + + {{#if this.canSplitToPM}} + + {{/if}} +
+ + {{#if this.existingTopic}} +

{{html-safe + (i18n + "topic.merge_topic.instructions" count=@model.selectedPostsCount + ) + }}

+
+ + + {{#if this.selectedTopicId}} +
+ + {{/if}} + + {{/if}} + + {{#if this.canSplitTopic}} + {{#if this.newTopic}} +

{{html-safe + (i18n + "topic.split_topic.instructions" count=@model.selectedPostsCount + ) + }}

+
+ + + + + + {{#if this.canAddTags}} + + + {{/if}} + + {{/if}} + {{/if}} + + {{#if this.canSplitTopic}} + {{#if this.newMessage}} +

+ {{html-safe + (i18n + "topic.move_to_new_message.instructions" + count=@model.selectedPostsCount + ) + }} +

+
+ + + + {{#if this.canTagMessages}} + + + {{/if}} + + {{/if}} + {{/if}} + {{/if}} + + <:footer> + + +
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js new file mode 100644 index 00000000000..8a7c2b8bc3e --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/move-to-topic.js @@ -0,0 +1,156 @@ +import Component from "@glimmer/component"; +import { action } from "@ember/object"; +import { tracked } from "@glimmer/tracking"; +import { mergeTopic, movePosts } from "discourse/models/topic"; +import DiscourseURL from "discourse/lib/url"; +import I18n from "I18n"; +import { isEmpty } from "@ember/utils"; +import { inject as service } from "@ember/service"; + +export default class MoveToTopic extends Component { + @service currentUser; + @service site; + + @tracked topicName; + @tracked saving = false; + @tracked categoryId; + @tracked tags; + @tracked participants = []; + @tracked chronologicalOrder = false; + @tracked selection = "new_topic"; + @tracked selectedTopicId; + + constructor() { + super(...arguments); + if (this.args.model.topic.isPrivateMessage) { + this.selection = this.canSplitToPM ? "new_message" : "existing_message"; + } else if (!this.canSplitTopic) { + this.selection = "existing_topic"; + } + } + + get newTopic() { + return this.selection === "new_topic"; + } + + get existingTopic() { + return this.selection === "existing_topic"; + } + + get newMessage() { + return this.selection === "new_message"; + } + + get existingMessage() { + return this.selection === "existing_message"; + } + + get buttonDisabled() { + return ( + this.saving || (isEmpty(this.selectedTopicId) && isEmpty(this.topicName)) + ); + } + + get buttonTitle() { + if (this.newTopic) { + return "topic.split_topic.title"; + } else if (this.existingTopic) { + return "topic.merge_topic.title"; + } else if (this.newMessage) { + return "topic.move_to_new_message.title"; + } else if (this.existingMessage) { + return "topic.move_to_existing_message.title"; + } else { + return "saving"; + } + } + + get canSplitTopic() { + return ( + !this.args.model.selectedAllPosts && + this.args.model.selectedPosts.length > 0 && + this.args.model.selectedPosts.sort( + (a, b) => a.post_number - b.post_number + )[0].post_type === this.site.get("post_types.regular") + ); + } + + get canSplitToPM() { + return this.canSplitTopic && this.currentUser?.admin; + } + + @action + performMove() { + if (this.newTopic) { + this.movePostsTo("newTopic"); + } else if (this.existingTopic) { + this.movePostsTo("existingTopic"); + } else if (this.newMessage) { + this.movePostsTo("newMessage"); + } else if (this.existingMessage) { + this.movePostsTo("existingMessage"); + } + } + + @action + async movePostsTo(type) { + this.saving = true; + this.flash = null; + let mergeOptions, moveOptions; + + if (type === "existingTopic") { + mergeOptions = { + destination_topic_id: this.selectedTopicId, + chronological_order: this.chronologicalOrder, + }; + moveOptions = { + post_ids: this.args.model.selectedPostIds, + ...mergeOptions, + }; + } else if (type === "existingMessage") { + mergeOptions = { + destination_topic_id: this.selectedTopicId, + participants: this.participants.join(","), + archetype: "private_message", + chronological_order: this.chronologicalOrder, + }; + moveOptions = { + post_ids: this.args.model.selectedPostIds, + ...mergeOptions, + }; + } else if (type === "newTopic") { + mergeOptions = {}; + moveOptions = { + title: this.topicName, + post_ids: this.args.model.selectedPostIds, + category_id: this.categoryId, + tags: this.tags, + }; + } else { + mergeOptions = {}; + moveOptions = { + title: this.topicName, + post_ids: this.args.model.selectedPostIds, + tags: this.tags, + archetype: "private_message", + }; + } + + try { + let result; + if (this.args.model.selectedAllPosts) { + result = await mergeTopic(this.args.model.topic.id, mergeOptions); + } else { + result = await movePosts(this.args.model.topic.id, moveOptions); + } + + this.args.closeModal(); + this.args.model.toggleMultiSelect(); + DiscourseURL.routeTo(result.url); + } catch { + this.flash = I18n.t("topic.move_to.error"); + } finally { + this.saving = false; + } + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/move-to-topic.js b/app/assets/javascripts/discourse/app/controllers/move-to-topic.js deleted file mode 100644 index 1ceed8f6725..00000000000 --- a/app/assets/javascripts/discourse/app/controllers/move-to-topic.js +++ /dev/null @@ -1,186 +0,0 @@ -import Controller, { inject as controller } from "@ember/controller"; -import { alias, equal } from "@ember/object/computed"; -import { mergeTopic, movePosts } from "discourse/models/topic"; -import DiscourseURL from "discourse/lib/url"; -import I18n from "I18n"; -import ModalFunctionality from "discourse/mixins/modal-functionality"; -import discourseComputed from "discourse-common/utils/decorators"; -import { flashAjaxError } from "discourse/lib/ajax-error"; -import { isEmpty } from "@ember/utils"; -import { next } from "@ember/runloop"; - -export default Controller.extend(ModalFunctionality, { - topicName: null, - saving: false, - categoryId: null, - tags: null, - canAddTags: alias("site.can_create_tag"), - canTagMessages: alias("site.can_tag_pms"), - selectedTopicId: null, - newTopic: equal("selection", "new_topic"), - existingTopic: equal("selection", "existing_topic"), - newMessage: equal("selection", "new_message"), - existingMessage: equal("selection", "existing_message"), - participants: null, - chronologicalOrder: false, - - init() { - this._super(...arguments); - - this.saveAttrNames = [ - "newTopic", - "existingTopic", - "newMessage", - "existingMessage", - ]; - - this.moveTypes = [ - "newTopic", - "existingTopic", - "newMessage", - "existingMessage", - ]; - }, - - topicController: controller("topic"), - selectedPostsCount: alias("topicController.selectedPostsCount"), - selectedAllPosts: alias("topicController.selectedAllPosts"), - selectedPosts: alias("topicController.selectedPosts"), - - @discourseComputed("saving", "selectedTopicId", "topicName") - buttonDisabled(saving, selectedTopicId, topicName) { - return saving || (isEmpty(selectedTopicId) && isEmpty(topicName)); - }, - - @discourseComputed( - "saving", - "newTopic", - "existingTopic", - "newMessage", - "existingMessage" - ) - buttonTitle(saving, newTopic, existingTopic, newMessage, existingMessage) { - if (newTopic) { - return I18n.t("topic.split_topic.title"); - } else if (existingTopic) { - return I18n.t("topic.merge_topic.title"); - } else if (newMessage) { - return I18n.t("topic.move_to_new_message.title"); - } else if (existingMessage) { - return I18n.t("topic.move_to_existing_message.title"); - } else { - return I18n.t("saving"); - } - }, - - onShow() { - this.setProperties({ - "modal.modalClass": "choose-topic-modal", - saving: false, - selection: "new_topic", - categoryId: null, - topicName: "", - tags: null, - participants: [], - selectedTopicId: null, - chronologicalOrder: false, - }); - - const isPrivateMessage = this.get("model.isPrivateMessage"); - if (isPrivateMessage) { - this.set( - "selection", - this.canSplitToPM ? "new_message" : "existing_message" - ); - } else if (!this.canSplitTopic) { - this.set("selection", "existing_topic"); - next(() => $("#choose-topic-title").focus()); - } - }, - - @discourseComputed("selectedAllPosts", "selectedPosts", "selectedPosts.[]") - canSplitTopic(selectedAllPosts, selectedPosts) { - return ( - !selectedAllPosts && - selectedPosts.length > 0 && - selectedPosts.sort((a, b) => a.post_number - b.post_number)[0] - .post_type === this.site.get("post_types.regular") - ); - }, - - @discourseComputed("canSplitTopic") - canSplitToPM(canSplitTopic) { - return canSplitTopic && this.currentUser && this.currentUser.admin; - }, - - actions: { - performMove() { - this.moveTypes.forEach((type) => { - if (this.get(type)) { - this.send("movePostsTo", type); - } - }); - }, - - movePostsTo(type) { - this.set("saving", true); - const topicId = this.get("model.id"); - let mergeOptions, moveOptions; - - if (type === "existingTopic") { - mergeOptions = { - destination_topic_id: this.selectedTopicId, - chronological_order: this.chronologicalOrder, - }; - moveOptions = Object.assign( - { post_ids: this.get("topicController.selectedPostIds") }, - mergeOptions - ); - } else if (type === "existingMessage") { - mergeOptions = { - destination_topic_id: this.selectedTopicId, - participants: this.participants.join(","), - archetype: "private_message", - chronological_order: this.chronologicalOrder, - }; - moveOptions = Object.assign( - { post_ids: this.get("topicController.selectedPostIds") }, - mergeOptions - ); - } else if (type === "newTopic") { - mergeOptions = {}; - moveOptions = { - title: this.topicName, - post_ids: this.get("topicController.selectedPostIds"), - category_id: this.categoryId, - tags: this.tags, - }; - } else { - mergeOptions = {}; - moveOptions = { - title: this.topicName, - post_ids: this.get("topicController.selectedPostIds"), - tags: this.tags, - archetype: "private_message", - }; - } - - const promise = this.get("topicController.selectedAllPosts") - ? mergeTopic(topicId, mergeOptions) - : movePosts(topicId, moveOptions); - - promise - .then((result) => { - this.send("closeModal"); - this.topicController.send("toggleMultiSelect"); - DiscourseURL.routeTo(result.url); - }) - .catch(flashAjaxError(this, I18n.t("topic.move_to.error"))) - .finally(() => { - this.set("saving", false); - }); - - return false; - }, - }, -}); diff --git a/app/assets/javascripts/discourse/app/routes/topic.js b/app/assets/javascripts/discourse/app/routes/topic.js index b7c71c74339..82cbab3e98a 100644 --- a/app/assets/javascripts/discourse/app/routes/topic.js +++ b/app/assets/javascripts/discourse/app/routes/topic.js @@ -17,6 +17,7 @@ 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"; import FlagModal from "discourse/components/modal/flag"; +import MoveToTopicModal from "discourse/components/modal/move-to-topic"; const SCROLL_DELAY = 500; @@ -212,9 +213,16 @@ const TopicRoute = DiscourseRoute.extend({ @action moveToTopic() { - showModal("move-to-topic", { - model: this.modelFor("topic"), - title: "topic.move_to.title", + const topicController = this.controllerFor("topic"); + this.modal.show(MoveToTopicModal, { + model: { + topic: this.modelFor("topic"), + selectedPostsCount: topicController.selectedPostsCount, + selectedAllPosts: topicController.selectedAllPosts, + selectedPosts: topicController.selectedPosts, + selectedPostIds: topicController.selectedPostIds, + toggleMultiSelect: topicController.toggleMultiSelect, + }, }); }, diff --git a/app/assets/javascripts/discourse/app/services/modal.js b/app/assets/javascripts/discourse/app/services/modal.js index 83160432aa5..cf5e35d6a3b 100644 --- a/app/assets/javascripts/discourse/app/services/modal.js +++ b/app/assets/javascripts/discourse/app/services/modal.js @@ -21,7 +21,6 @@ const KNOWN_LEGACY_MODALS = [ "grant-badge", "group-default-notifications", "login", - "move-to-topic", "raw-email", "reject-reason-reviewable", "reorder-categories", diff --git a/app/assets/javascripts/discourse/app/templates/modal/move-to-topic.hbs b/app/assets/javascripts/discourse/app/templates/modal/move-to-topic.hbs deleted file mode 100644 index 7ca7f1cad06..00000000000 --- a/app/assets/javascripts/discourse/app/templates/modal/move-to-topic.hbs +++ /dev/null @@ -1,212 +0,0 @@ - - - {{#if this.model.isPrivateMessage}} -
- {{#if this.canSplitToPM}} - - {{/if}} - - -
- - {{#if this.canSplitTopic}} - {{#if this.newMessage}} -

{{html-safe - (i18n - "topic.move_to_new_message.instructions" - count=this.selectedPostsCount - ) - }}

-
- - - - {{#if this.canTagMessages}} - - - {{/if}} - - {{/if}} - {{/if}} - - {{#if this.existingMessage}} -

{{html-safe - (i18n - "topic.move_to_existing_message.instructions" - count=this.selectedPostsCount - ) - }}

-
- - - - - - {{#if this.selectedTopicId}} -
- - {{/if}} - - {{/if}} - - {{else}} - -
- {{#if this.canSplitTopic}} - - {{/if}} - - - - {{#if this.canSplitToPM}} - - {{/if}} -
- - {{#if this.existingTopic}} -

{{html-safe - (i18n "topic.merge_topic.instructions" count=this.selectedPostsCount) - }}

-
- - - {{#if this.selectedTopicId}} -
- - {{/if}} - - {{/if}} - - {{#if this.canSplitTopic}} - {{#if this.newTopic}} -

{{html-safe - (i18n - "topic.split_topic.instructions" count=this.selectedPostsCount - ) - }}

-
- - - - - - {{#if this.canAddTags}} - - - {{/if}} - - {{/if}} - {{/if}} - - {{#if this.canSplitTopic}} - {{#if this.newMessage}} -

{{html-safe - (i18n - "topic.move_to_new_message.instructions" - count=this.selectedPostsCount - ) - }}

-
- - - - {{#if this.canTagMessages}} - - - {{/if}} - - {{/if}} - {{/if}} - {{/if}} - -
- - \ No newline at end of file