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}}
+ {{/if}}
+
+ {{#if this.existingMessage}}
+
+ {{html-safe
+ (i18n
+ "topic.move_to_existing_message.instructions"
+ count=@model.selectedPostsCount
+ )
+ }}
+
+
+ {{/if}}
+
+ {{else}}
+
+
+ {{#if this.canSplitTopic}}
+
+ {{/if}}
+
+
+
+ {{#if this.canSplitToPM}}
+
+ {{/if}}
+
+
+ {{#if this.existingTopic}}
+ {{html-safe
+ (i18n
+ "topic.merge_topic.instructions" count=@model.selectedPostsCount
+ )
+ }}
+
+ {{/if}}
+
+ {{#if this.canSplitTopic}}
+ {{#if this.newTopic}}
+ {{html-safe
+ (i18n
+ "topic.split_topic.instructions" count=@model.selectedPostsCount
+ )
+ }}
+
+ {{/if}}
+ {{/if}}
+
+ {{#if this.canSplitTopic}}
+ {{#if this.newMessage}}
+
+ {{html-safe
+ (i18n
+ "topic.move_to_new_message.instructions"
+ count=@model.selectedPostsCount
+ )
+ }}
+
+
+ {{/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}}
- {{/if}}
-
- {{#if this.existingMessage}}
- {{html-safe
- (i18n
- "topic.move_to_existing_message.instructions"
- count=this.selectedPostsCount
- )
- }}
-
- {{/if}}
-
- {{else}}
-
-
- {{#if this.canSplitTopic}}
-
- {{/if}}
-
-
-
- {{#if this.canSplitToPM}}
-
- {{/if}}
-
-
- {{#if this.existingTopic}}
- {{html-safe
- (i18n "topic.merge_topic.instructions" count=this.selectedPostsCount)
- }}
-
- {{/if}}
-
- {{#if this.canSplitTopic}}
- {{#if this.newTopic}}
- {{html-safe
- (i18n
- "topic.split_topic.instructions" count=this.selectedPostsCount
- )
- }}
-
- {{/if}}
- {{/if}}
-
- {{#if this.canSplitTopic}}
- {{#if this.newMessage}}
- {{html-safe
- (i18n
- "topic.move_to_new_message.instructions"
- count=this.selectedPostsCount
- )
- }}
-
- {{/if}}
- {{/if}}
- {{/if}}
-
-
-
-
\ No newline at end of file