diff --git a/app/assets/javascripts/discourse/app/components/card-container.hbs b/app/assets/javascripts/discourse/app/components/card-container.hbs
index 9bde984d351..06434655848 100644
--- a/app/assets/javascripts/discourse/app/components/card-container.hbs
+++ b/app/assets/javascripts/discourse/app/components/card-container.hbs
@@ -7,7 +7,6 @@
@showUser={{this.showUser}}
@filterPosts={{this.filterPosts}}
@composePrivateMessage={{route-action "composePrivateMessage"}}
- @createNewMessageViaParams={{route-action "createNewMessageViaParams"}}
role="dialog"
/>
@@ -15,5 +14,4 @@
@topic={{this.topic.model}}
@showUser={{this.showUser}}
@showGroup={{this.showGroup}}
- @createNewMessageViaParams={{route-action "createNewMessageViaParams"}}
/>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/composer-action-title.hbs b/app/assets/javascripts/discourse/app/components/composer-action-title.hbs
index 70c27a96f27..09de2cf2e82 100644
--- a/app/assets/javascripts/discourse/app/components/composer-action-title.hbs
+++ b/app/assets/javascripts/discourse/app/components/composer-action-title.hbs
@@ -2,8 +2,6 @@
@composerModel={{this.model}}
@replyOptions={{this.model.replyOptions}}
@canWhisper={{this.canWhisper}}
- @openComposer={{this.openComposer}}
- @closeComposer={{this.closeComposer}}
@action={{this.model.action}}
@tabindex={{this.tabindex}}
@topic={{this.model.topic}}
diff --git a/app/assets/javascripts/discourse/app/components/composer-container.hbs b/app/assets/javascripts/discourse/app/components/composer-container.hbs
index 8e19f89aa30..0b4e430133c 100644
--- a/app/assets/javascripts/discourse/app/components/composer-container.hbs
+++ b/app/assets/javascripts/discourse/app/components/composer-container.hbs
@@ -46,8 +46,6 @@
diff --git a/app/assets/javascripts/discourse/app/components/group-card-contents.js b/app/assets/javascripts/discourse/app/components/group-card-contents.js
index bdadccc22c0..173bfe6e247 100644
--- a/app/assets/javascripts/discourse/app/components/group-card-contents.js
+++ b/app/assets/javascripts/discourse/app/components/group-card-contents.js
@@ -8,10 +8,12 @@ import discourseComputed from "discourse-common/utils/decorators";
import { groupPath } from "discourse/lib/url";
import { setting } from "discourse/lib/computed";
import { modKeysPressed } from "discourse/lib/utilities";
+import { inject as service } from "@ember/service";
const maxMembersToDisplay = 10;
export default Component.extend(CardContentsBase, CleansUp, {
+ composer: service(),
elementId: "group-card",
mentionSelector: "a.mention-group",
classNames: ["no-bg", "group-card"],
@@ -105,7 +107,7 @@ export default Component.extend(CardContentsBase, CleansUp, {
},
messageGroup() {
- this.createNewMessageViaParams({
+ this.composer.openNewMessage({
recipients: this.get("group.name"),
hasGroups: true,
});
diff --git a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
index 27e3bc8218d..46685faa991 100644
--- a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
+++ b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
@@ -18,10 +18,10 @@ export default class TopicsController extends DiscoveryController.extend(
DismissTopics
) {
@service router;
+ @service composer;
@controller discovery;
period = null;
- canCreateTopicOnCategory = null;
selected = null;
expandGloballyPinned = false;
expandAllPinned = false;
diff --git a/app/assets/javascripts/discourse/app/controllers/group.js b/app/assets/javascripts/discourse/app/controllers/group.js
index babbd63f1e3..28e5fb3c21d 100644
--- a/app/assets/javascripts/discourse/app/controllers/group.js
+++ b/app/assets/javascripts/discourse/app/controllers/group.js
@@ -22,6 +22,7 @@ export default Controller.extend({
dialog: service(),
currentUser: service(),
router: service(),
+ composer: service(),
counts: null,
showing: "members",
@@ -130,7 +131,7 @@ export default Controller.extend({
@action
messageGroup() {
- this.send("createNewMessageViaParams", {
+ this.composer.openNewMessage({
recipients: this.get("model.name"),
hasGroups: true,
});
diff --git a/app/assets/javascripts/discourse/app/controllers/navigation/categories.js b/app/assets/javascripts/discourse/app/controllers/navigation/categories.js
index e39a8c8a750..2043c6721a6 100644
--- a/app/assets/javascripts/discourse/app/controllers/navigation/categories.js
+++ b/app/assets/javascripts/discourse/app/controllers/navigation/categories.js
@@ -1,6 +1,8 @@
import NavigationDefaultController from "discourse/controllers/navigation/default";
import { inject as controller } from "@ember/controller";
+import { inject as service } from "@ember/service";
export default class NavigationCategoriesController extends NavigationDefaultController {
+ @service composer;
@controller("discovery/categories") discoveryCategories;
}
diff --git a/app/assets/javascripts/discourse/app/controllers/navigation/category.js b/app/assets/javascripts/discourse/app/controllers/navigation/category.js
index c79ca3e9a97..1059ed62035 100644
--- a/app/assets/javascripts/discourse/app/controllers/navigation/category.js
+++ b/app/assets/javascripts/discourse/app/controllers/navigation/category.js
@@ -2,8 +2,11 @@ import NavigationDefaultController from "discourse/controllers/navigation/defaul
import { calculateFilterMode } from "discourse/lib/filter-mode";
import { dependentKeyCompat } from "@ember/object/compat";
import { tracked } from "@glimmer/tracking";
+import { inject as service } from "@ember/service";
export default class NavigationCategoryController extends NavigationDefaultController {
+ @service composer;
+
@tracked category;
@tracked filterType;
@tracked noSubcategories;
@@ -16,4 +19,22 @@ export default class NavigationCategoryController extends NavigationDefaultContr
noSubcategories: this.noSubcategories,
});
}
+
+ get createTopicTargetCategory() {
+ if (this.category?.canCreateTopic) {
+ return this.category;
+ }
+
+ if (this.siteSettings.default_subcategory_on_read_only_category) {
+ return this.category?.subcategoryWithCreateTopicPermission;
+ }
+ }
+
+ get enableCreateTopicButton() {
+ return !!this.createTopicTargetCategory;
+ }
+
+ get canCreateTopic() {
+ return this.currentUser?.can_create_topic;
+ }
}
diff --git a/app/assets/javascripts/discourse/app/controllers/navigation/default.js b/app/assets/javascripts/discourse/app/controllers/navigation/default.js
index 8777c1b11c3..aaf9c70dcb7 100644
--- a/app/assets/javascripts/discourse/app/controllers/navigation/default.js
+++ b/app/assets/javascripts/discourse/app/controllers/navigation/default.js
@@ -7,6 +7,7 @@ import { tracked } from "@glimmer/tracking";
export default class NavigationDefaultController extends Controller {
@service router;
+ @service composer;
@controller discovery;
@tracked category;
diff --git a/app/assets/javascripts/discourse/app/mixins/open-composer.js b/app/assets/javascripts/discourse/app/mixins/open-composer.js
deleted file mode 100644
index 68dd5f71c5e..00000000000
--- a/app/assets/javascripts/discourse/app/mixins/open-composer.js
+++ /dev/null
@@ -1,77 +0,0 @@
-// This mixin allows a route to open the composer
-import Composer from "discourse/models/composer";
-import Mixin from "@ember/object/mixin";
-import { getOwner } from "discourse-common/lib/get-owner";
-
-export default Mixin.create({
- openComposer(controller) {
- let categoryId = controller.get("category.id");
-
- if (
- this.siteSettings.default_subcategory_on_read_only_category &&
- !controller.canCreateTopicOnCategory
- ) {
- if (controller.canCreateTopicOnSubCategory) {
- categoryId = controller.get("defaultSubcategory.id");
- } else {
- categoryId = this.siteSettings.default_composer_category;
- }
- }
-
- if (
- categoryId &&
- !this.siteSettings.default_subcategory_on_read_only_category &&
- controller.category.isUncategorizedCategory &&
- !this.siteSettings.allow_uncategorized_topics
- ) {
- categoryId = null;
- }
-
- getOwner(this)
- .lookup("service:composer")
- .open({
- prioritizedCategoryId: categoryId,
- topicCategoryId: categoryId,
- action: Composer.CREATE_TOPIC,
- draftKey: controller.get("model.draft_key") || Composer.NEW_TOPIC_KEY,
- draftSequence: controller.get("model.draft_sequence") || 0,
- });
- },
-
- openComposerWithTopicParams(
- controller,
- topicTitle,
- topicBody,
- topicCategoryId,
- topicTags
- ) {
- getOwner(this)
- .lookup("service:composer")
- .open({
- action: Composer.CREATE_TOPIC,
- topicTitle,
- topicBody,
- topicCategoryId,
- topicTags,
- draftKey: controller.get("model.draft_key") || Composer.NEW_TOPIC_KEY,
- draftSequence: controller.get("model.draft_sequence"),
- });
- },
-
- openComposerWithMessageParams({
- recipients = "",
- topicTitle = "",
- topicBody = "",
- hasGroups = false,
- } = {}) {
- getOwner(this).lookup("service:composer").open({
- action: Composer.PRIVATE_MESSAGE,
- recipients,
- topicTitle,
- topicBody,
- archetypeId: "private_message",
- draftKey: Composer.NEW_PRIVATE_MESSAGE_KEY,
- hasGroups,
- });
- },
-});
diff --git a/app/assets/javascripts/discourse/app/models/category.js b/app/assets/javascripts/discourse/app/models/category.js
index 8bb0f762d92..53c81f194ee 100644
--- a/app/assets/javascripts/discourse/app/models/category.js
+++ b/app/assets/javascripts/discourse/app/models/category.js
@@ -345,6 +345,16 @@ const Category = RestModel.extend({
isUncategorizedCategory(id) {
return Category.isUncategorized(id);
},
+
+ get canCreateTopic() {
+ return this.permission === PermissionType.FULL;
+ },
+
+ get subcategoryWithCreateTopicPermission() {
+ return this.subcategories?.find(
+ (subcategory) => subcategory.canCreateTopic
+ );
+ },
});
let _uncategorized;
diff --git a/app/assets/javascripts/discourse/app/routes/application.js b/app/assets/javascripts/discourse/app/routes/application.js
index 6837988ec5a..01afa81f3a2 100644
--- a/app/assets/javascripts/discourse/app/routes/application.js
+++ b/app/assets/javascripts/discourse/app/routes/application.js
@@ -3,7 +3,6 @@ import Category from "discourse/models/category";
import Composer from "discourse/models/composer";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
-import OpenComposer from "discourse/mixins/open-composer";
import { ajax } from "discourse/lib/ajax";
import { findAll } from "discourse/models/login-method";
import { getOwner } from "discourse-common/lib/get-owner";
@@ -17,6 +16,7 @@ import { action } from "@ember/object";
import KeyboardShortcutsHelp from "discourse/components/modal/keyboard-shortcuts-help";
import NotActivatedModal from "../components/modal/not-activated";
import ForgotPassword from "discourse/components/modal/forgot-password";
+import deprecated from "discourse-common/lib/deprecated";
function unlessReadOnly(method, message) {
return function () {
@@ -38,7 +38,7 @@ function unlessStrictlyReadOnly(method, message) {
};
}
-const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
+const ApplicationRoute = DiscourseRoute.extend({
siteTitle: setting("title"),
shortSiteDescription: setting("short_site_description"),
documentTitle: service(),
@@ -190,14 +190,17 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
user.checkEmail();
},
- createNewTopicViaParams(title, body, category_id, tags) {
- this.openComposerWithTopicParams(
- this.controllerFor("discovery/topics"),
+ createNewTopicViaParams(title, body, categoryId, tags) {
+ deprecated(
+ "createNewTopicViaParam on the application route is deprecated. Use the composer service instead",
+ { id: "discourse.createNewTopicViaParams" }
+ );
+ getOwner(this).lookup("service:composer").openNewTopic({
title,
body,
- category_id,
- tags
- );
+ categoryId,
+ tags,
+ });
},
createNewMessageViaParams({
@@ -206,10 +209,14 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
topicBody = "",
hasGroups = false,
} = {}) {
- this.openComposerWithMessageParams({
+ deprecated(
+ "createNewMessageViaParams on the application route is deprecated. Use the composer service instead",
+ { id: "discourse.createNewMessageViaParams" }
+ );
+ getOwner(this).lookup("service:composer").openNewMessage({
recipients,
- topicTitle,
- topicBody,
+ title: topicTitle,
+ body: topicBody,
hasGroups,
});
},
diff --git a/app/assets/javascripts/discourse/app/routes/build-category-route.js b/app/assets/javascripts/discourse/app/routes/build-category-route.js
index 739e7ff5da5..ea6bf0a86b4 100644
--- a/app/assets/javascripts/discourse/app/routes/build-category-route.js
+++ b/app/assets/javascripts/discourse/app/routes/build-category-route.js
@@ -13,7 +13,6 @@ import Category from "discourse/models/category";
import CategoryList from "discourse/models/category-list";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
-import PermissionType from "discourse/models/permission-type";
import TopicList from "discourse/models/topic-list";
import { action } from "@ember/object";
import PreloadStore from "discourse/lib/preload-store";
@@ -152,33 +151,7 @@ class AbstractCategoryRoute extends DiscourseRoute {
setupController(controller, model) {
const topics = this.topics,
- category = model.category,
- canCreateTopic = topics.get("can_create_topic");
-
- let canCreateTopicOnCategory =
- canCreateTopic && category.get("permission") === PermissionType.FULL;
- let cannotCreateTopicOnCategory = !canCreateTopicOnCategory;
- let defaultSubcategory;
- let canCreateTopicOnSubCategory;
-
- if (this.siteSettings.default_subcategory_on_read_only_category) {
- cannotCreateTopicOnCategory = false;
-
- if (!canCreateTopicOnCategory && category.subcategories) {
- defaultSubcategory = category.subcategories.find((subcategory) => {
- return subcategory.get("permission") === PermissionType.FULL;
- });
- canCreateTopicOnSubCategory = !!defaultSubcategory;
- }
- }
-
- this.controllerFor("navigation/category").setProperties({
- canCreateTopicOnCategory,
- cannotCreateTopicOnCategory,
- canCreateTopic,
- canCreateTopicOnSubCategory,
- defaultSubcategory,
- });
+ category = model.category;
let topicOpts = {
model: topics,
@@ -189,10 +162,6 @@ class AbstractCategoryRoute extends DiscourseRoute {
selected: [],
noSubcategories: this.routeConfig && !!this.routeConfig.no_subcategories,
expandAllPinned: true,
- canCreateTopic,
- canCreateTopicOnCategory,
- canCreateTopicOnSubCategory,
- defaultSubcategory,
};
const p = category.get("params");
diff --git a/app/assets/javascripts/discourse/app/routes/discourse.js b/app/assets/javascripts/discourse/app/routes/discourse.js
index da07704a23c..4deca988c30 100644
--- a/app/assets/javascripts/discourse/app/routes/discourse.js
+++ b/app/assets/javascripts/discourse/app/routes/discourse.js
@@ -1,9 +1,8 @@
-import Composer from "discourse/models/composer";
-import Draft from "discourse/models/draft";
import Route from "@ember/routing/route";
import { once } from "@ember/runloop";
import { seenUser } from "discourse/lib/user-presence";
import { getOwner } from "discourse-common/lib/get-owner";
+import deprecated from "discourse-common/lib/deprecated";
const DiscourseRoute = Route.extend({
showFooter: false,
@@ -54,24 +53,14 @@ const DiscourseRoute = Route.extend({
},
openTopicDraft() {
- const composer = getOwner(this).lookup("service:composer");
-
- if (
- composer.get("model.action") === Composer.CREATE_TOPIC &&
- composer.get("model.draftKey") === Composer.NEW_TOPIC_KEY
- ) {
- composer.set("model.composeState", Composer.OPEN);
- } else {
- Draft.get(Composer.NEW_TOPIC_KEY).then((data) => {
- if (data.draft) {
- composer.open({
- action: Composer.CREATE_TOPIC,
- draft: data.draft,
- draftKey: Composer.NEW_TOPIC_KEY,
- draftSequence: data.draft_sequence,
- });
- }
- });
+ deprecated(
+ "DiscourseRoute#openTopicDraft is deprecated. Inject the composer service and call openNewTopic instead",
+ { id: "discourse.open-topic-draft" }
+ );
+ if (this.currentUser?.has_topic_draft) {
+ return getOwner(this)
+ .lookup("service:composer")
+ .openNewTopic({ preferDraft: true });
}
},
diff --git a/app/assets/javascripts/discourse/app/routes/discovery-categories.js b/app/assets/javascripts/discourse/app/routes/discovery-categories.js
index 59f9e79a085..32206de29de 100644
--- a/app/assets/javascripts/discourse/app/routes/discovery-categories.js
+++ b/app/assets/javascripts/discourse/app/routes/discovery-categories.js
@@ -2,7 +2,6 @@ import CategoryList from "discourse/models/category-list";
import DiscourseRoute from "discourse/routes/discourse";
import EmberObject, { action } from "@ember/object";
import I18n from "I18n";
-import OpenComposer from "discourse/mixins/open-composer";
import PreloadStore from "discourse/lib/preload-store";
import TopicList from "discourse/models/topic-list";
import { ajax } from "discourse/lib/ajax";
@@ -13,9 +12,7 @@ import showModal from "discourse/lib/show-modal";
import Session from "discourse/models/session";
import { inject as service } from "@ember/service";
-export default class DiscoveryCategoriesRoute extends DiscourseRoute.extend(
- OpenComposer
-) {
+export default class DiscoveryCategoriesRoute extends DiscourseRoute {
@service router;
renderTemplate() {
@@ -157,15 +154,6 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute.extend(
showModal("reorder-categories");
}
- @action
- createTopic() {
- if (this.get("currentUser.has_topic_draft")) {
- this.openTopicDraft();
- } else {
- this.openComposer(this.controllerFor("discovery/categories"));
- }
- }
-
@action
didTransition() {
next(() => this.controllerFor("application").set("showFooter", true));
diff --git a/app/assets/javascripts/discourse/app/routes/discovery.js b/app/assets/javascripts/discourse/app/routes/discovery.js
index 5af1c084ba7..b0faced7a57 100644
--- a/app/assets/javascripts/discourse/app/routes/discovery.js
+++ b/app/assets/javascripts/discourse/app/routes/discovery.js
@@ -1,5 +1,4 @@
import DiscourseRoute from "discourse/routes/discourse";
-import OpenComposer from "discourse/mixins/open-composer";
import User from "discourse/models/user";
import { setTopicList } from "discourse/lib/topic-list-tracker";
import { action } from "@ember/object";
@@ -9,9 +8,7 @@ import { resetCachedTopicList } from "discourse/lib/cached-topic-list";
The parent route for all discovery routes.
Handles the logic for showing the loading spinners.
**/
-export default class DiscoveryRoute extends DiscourseRoute.extend(
- OpenComposer
-) {
+export default class DiscoveryRoute extends DiscourseRoute {
queryParams = {
filter: { refreshModel: true },
};
@@ -74,15 +71,6 @@ export default class DiscoveryRoute extends DiscourseRoute.extend(
topic.clearPin();
}
- @action
- createTopic() {
- if (this.get("currentUser.has_topic_draft")) {
- this.openTopicDraft();
- } else {
- this.openComposer(this.controllerFor("discovery/topics"));
- }
- }
-
@action
dismissReadTopics(dismissTopics) {
const operationType = dismissTopics ? "topics" : "posts";
diff --git a/app/assets/javascripts/discourse/app/routes/new-message.js b/app/assets/javascripts/discourse/app/routes/new-message.js
index c9794ce116f..96f4143caa2 100644
--- a/app/assets/javascripts/discourse/app/routes/new-message.js
+++ b/app/assets/javascripts/discourse/app/routes/new-message.js
@@ -7,6 +7,7 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
dialog: service(),
+ composer: service(),
beforeModel(transition) {
const params = transition.to.queryParams;
@@ -14,12 +15,12 @@ export default DiscourseRoute.extend({
const groupName = params.groupname || params.group_name;
if (this.currentUser) {
- this.replaceWith("discovery.latest").then((e) => {
+ this.replaceWith("discovery.latest").then(() => {
if (params.username) {
- e.send("createNewMessageViaParams", {
+ this.composer.openNewMessage({
recipients: params.username,
- topicTitle: params.title,
- topicBody: params.body,
+ title: params.title,
+ body: params.body,
});
} else if (groupName) {
// send a message to a group
@@ -27,10 +28,10 @@ export default DiscourseRoute.extend({
.then((result) => {
if (result.messageable) {
next(() =>
- e.send("createNewMessageViaParams", {
+ this.composer.openNewMessage({
recipients: groupName,
- topicTitle: params.title,
- topicBody: params.body,
+ title: params.title,
+ body: params.body,
})
);
} else {
@@ -41,9 +42,9 @@ export default DiscourseRoute.extend({
})
.catch(() => this.dialog.alert(I18n.t("generic_error")));
} else {
- e.send("createNewMessageViaParams", {
- topicTitle: params.title,
- topicBody: params.body,
+ this.composer.openNewMessage({
+ title: params.title,
+ body: params.body,
});
}
});
diff --git a/app/assets/javascripts/discourse/app/routes/new-topic.js b/app/assets/javascripts/discourse/app/routes/new-topic.js
index f66848f90b9..e4928b5c56e 100644
--- a/app/assets/javascripts/discourse/app/routes/new-topic.js
+++ b/app/assets/javascripts/discourse/app/routes/new-topic.js
@@ -2,76 +2,78 @@ import Category from "discourse/models/category";
import DiscourseRoute from "discourse/routes/discourse";
import cookie from "discourse/lib/cookie";
import { next } from "@ember/runloop";
+import { inject as service } from "@ember/service";
+
+export default class extends DiscourseRoute {
+ @service composer;
-export default DiscourseRoute.extend({
beforeModel(transition) {
if (this.currentUser) {
- let category, categoryId;
-
- if (transition.to.queryParams.category_id) {
- categoryId = transition.to.queryParams.category_id;
- category = Category.findById(categoryId);
- } else if (transition.to.queryParams.category) {
- const splitCategory = transition.to.queryParams.category.split("/");
-
- category = this._getCategory(
- splitCategory[0],
- splitCategory[1],
- "nameLower"
- );
-
- if (!category) {
- category = this._getCategory(
- splitCategory[0],
- splitCategory[1],
- "slug"
- );
- }
-
- if (category) {
- categoryId = category.id;
- }
- }
+ const category = this.parseCategoryFromTransition(transition);
if (category) {
- let route = "discovery.category";
- let params = { category, id: category.id };
-
- this.replaceWith(route, params).then((e) => {
- if (this.controllerFor("navigation/category").canCreateTopic) {
- this._sendTransition(e, transition, categoryId);
+ this.replaceWith("discovery.category", {
+ category,
+ id: category.id,
+ }).then(() => {
+ if (this.currentUser.can_create_topic) {
+ this.openComposer({ transition, category });
}
});
+ } else if (transition.from) {
+ // Navigation from another ember route
+ transition.abort();
+ this.openComposer({ transition });
} else {
- if (transition.from) {
- transition.abort();
- this.send("createNewTopicViaParams");
- } else {
- this.replaceWith("discovery.latest").then((e) => {
- if (this.controllerFor("navigation/default").canCreateTopic) {
- this._sendTransition(e, transition);
- }
- });
- }
+ this.replaceWith("discovery.latest").then(() => {
+ if (this.currentUser.can_create_topic) {
+ this.openComposer({ transition });
+ }
+ });
}
} else {
// User is not logged in
cookie("destination_url", window.location.href);
this.replaceWith("login");
}
- },
+ }
- _sendTransition(event, transition, categoryId) {
+ openComposer({ transition, category }) {
next(() => {
- event.send(
- "createNewTopicViaParams",
- transition.to.queryParams.title,
- transition.to.queryParams.body,
- categoryId,
- transition.to.queryParams.tags
- );
+ this.composer.openNewTopic({
+ title: transition.to.queryParams.title,
+ body: transition.to.queryParams.body,
+ category,
+ tags: transition.to.queryParams.tags,
+ });
});
- },
+ }
+
+ parseCategoryFromTransition(transition) {
+ let category;
+
+ if (transition.to.queryParams.category_id) {
+ const categoryId = transition.to.queryParams.category_id;
+ category = Category.findById(categoryId);
+ } else if (transition.to.queryParams.category) {
+ const splitCategory = transition.to.queryParams.category.split("/");
+
+ category = this._getCategory(
+ splitCategory[0],
+ splitCategory[1],
+ "nameLower"
+ );
+
+ if (!category) {
+ category = this._getCategory(
+ splitCategory[0],
+ splitCategory[1],
+ "slug"
+ );
+ }
+ }
+ return category;
+ }
_getCategory(mainCategory, subCategory, type) {
let category;
@@ -92,5 +94,5 @@ export default DiscourseRoute.extend({
}
return category;
- },
-});
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/services/composer.js b/app/assets/javascripts/discourse/app/services/composer.js
index 53d5a1aa591..e58c74bc4a5 100644
--- a/app/assets/javascripts/discourse/app/services/composer.js
+++ b/app/assets/javascripts/discourse/app/services/composer.js
@@ -1,4 +1,9 @@
-import Composer, { SAVE_ICONS, SAVE_LABELS } from "discourse/models/composer";
+import Composer, {
+ CREATE_TOPIC,
+ NEW_TOPIC_KEY,
+ SAVE_ICONS,
+ SAVE_LABELS,
+} from "discourse/models/composer";
import EmberObject, { action, computed } from "@ember/object";
import { alias, and, or, reads } from "@ember/object/computed";
import {
@@ -567,33 +572,6 @@ export default class ComposerService extends Service {
this.close();
}
- @action
- async openComposer(options, post, topic) {
- await this.open(options);
-
- let url = post?.url || topic?.url;
- const topicTitle = topic?.title;
-
- if (!url || !topicTitle) {
- return;
- }
-
- url = `${location.protocol}//${location.host}${url}`;
- const link = `[${escapeExpression(topicTitle)}](${url})`;
- const continueDiscussion = I18n.t("post.continue_discussion", {
- postLink: link,
- });
-
- const reply = this.get("model.reply");
- if (reply?.includes(continueDiscussion)) {
- return;
- }
-
- this.model.prependText(continueDiscussion, {
- new_line: true,
- });
- }
-
@action
onPopupMenuAction(menuAction) {
return (
@@ -1339,6 +1317,62 @@ export default class ComposerService extends Service {
}
}
+ async #openNewTopicDraft() {
+ if (
+ this.model?.action === Composer.CREATE_TOPIC &&
+ this.model?.draftKey === Composer.NEW_TOPIC_KEY
+ ) {
+ this.set("model.composeState", Composer.OPEN);
+ } else {
+ const data = await Draft.get(Composer.NEW_TOPIC_KEY);
+ if (data.draft) {
+ return this.open({
+ action: Composer.CREATE_TOPIC,
+ draft: data.draft,
+ draftKey: Composer.NEW_TOPIC_KEY,
+ draftSequence: data.draft_sequence,
+ });
+ }
+ }
+ }
+
+ @action
+ async openNewTopic({
+ title,
+ body,
+ category,
+ tags,
+ preferDraft = false,
+ } = {}) {
+ if (preferDraft && this.currentUser.has_topic_draft) {
+ return this.#openNewTopicDraft();
+ } else {
+ return this.open({
+ prioritizedCategoryId: category?.id,
+ topicCategoryId: category?.id,
+ topicTitle: title,
+ topicBody: body,
+ topicTags: tags,
+ action: CREATE_TOPIC,
+ draftKey: NEW_TOPIC_KEY,
+ draftSequence: 0,
+ });
+ }
+ }
+
+ @action
+ async openNewMessage({ title, body, recipients, hasGroups }) {
+ return this.open({
+ action: Composer.PRIVATE_MESSAGE,
+ recipients,
+ topicTitle: title,
+ topicBody: body,
+ archetypeId: "private_message",
+ draftKey: Composer.NEW_PRIVATE_MESSAGE_KEY,
+ hasGroups,
+ });
+ }
+
// Given a potential instance and options, set the model for this composer.
async _setModel(optionalComposerModel, opts) {
this.set("linkLookup", null);
@@ -1406,6 +1440,12 @@ export default class ComposerService extends Service {
this.model.set("reply", opts.topicBody);
}
+ if (opts.prependText && !this.model.reply?.includes(opts.prependText)) {
+ this.model.prependText(opts.prependText, {
+ new_line: true,
+ });
+ }
+
const defaultComposerHeight = this._getDefaultComposerHeight();
this.set("model.composerHeight", defaultComposerHeight);
diff --git a/app/assets/javascripts/discourse/app/templates/discovery.hbs b/app/assets/javascripts/discourse/app/templates/discovery.hbs
index 2eaecdf418a..1777b6050ea 100644
--- a/app/assets/javascripts/discourse/app/templates/discovery.hbs
+++ b/app/assets/javascripts/discourse/app/templates/discovery.hbs
@@ -3,7 +3,7 @@
{{#unless this.viewingCategoriesList}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs
index c4c1129178a..2f4b89f3d8d 100644
--- a/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs
+++ b/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs
@@ -119,9 +119,12 @@
@message={{this.footerMessage}}
>
{{#if this.latest}}
- {{#if this.canCreateTopicOnCategory}}
+ {{#if this.category.canCreateTopic}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/app/templates/mobile/discovery/topics.hbs b/app/assets/javascripts/discourse/app/templates/mobile/discovery/topics.hbs
index 869f996736a..32eb6348fbe 100644
--- a/app/assets/javascripts/discourse/app/templates/mobile/discovery/topics.hbs
+++ b/app/assets/javascripts/discourse/app/templates/mobile/discovery/topics.hbs
@@ -76,9 +76,12 @@
@message={{this.footerMessage}}
>
{{#if this.latest}}
- {{#if this.canCreateTopicOnCategory}}
+ {{#if this.category.canCreateTopic}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/app/templates/navigation/categories.hbs b/app/assets/javascripts/discourse/app/templates/navigation/categories.hbs
index 4c37aa422ed..9ddc0f1ed95 100644
--- a/app/assets/javascripts/discourse/app/templates/navigation/categories.hbs
+++ b/app/assets/javascripts/discourse/app/templates/navigation/categories.hbs
@@ -6,6 +6,6 @@
@reorderCategories={{route-action "reorderCategories"}}
@canCreateTopic={{this.canCreateTopic}}
@hasDraft={{this.currentUser.has_topic_draft}}
- @createTopic={{route-action "createTopic"}}
+ @createTopic={{fn this.composer.openNewTopic (hash preferDraft=true)}}
/>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/templates/navigation/category.hbs b/app/assets/javascripts/discourse/app/templates/navigation/category.hbs
index 593732b95c6..8d63ff9deb6 100644
--- a/app/assets/javascripts/discourse/app/templates/navigation/category.hbs
+++ b/app/assets/javascripts/discourse/app/templates/navigation/category.hbs
@@ -23,8 +23,11 @@
@filterMode={{this.filterMode}}
@noSubcategories={{this.noSubcategories}}
@canCreateTopic={{this.canCreateTopic}}
- @createTopic={{route-action "createTopic"}}
- @createTopicDisabled={{this.cannotCreateTopicOnCategory}}
+ @createTopic={{fn
+ this.composer.openNewTopic
+ (hash category=this.createTopicTargetCategory preferDraft=true)
+ }}
+ @createTopicDisabled={{not this.enableCreateTopicButton}}
@hasDraft={{this.currentUser.has_topic_draft}}
@editCategory={{route-action "editCategory" this.category}}
/>
diff --git a/app/assets/javascripts/discourse/app/templates/navigation/default.hbs b/app/assets/javascripts/discourse/app/templates/navigation/default.hbs
index c075d552437..3ce41264012 100644
--- a/app/assets/javascripts/discourse/app/templates/navigation/default.hbs
+++ b/app/assets/javascripts/discourse/app/templates/navigation/default.hbs
@@ -3,7 +3,7 @@
@filterMode={{this.filterMode}}
@canCreateTopic={{this.canCreateTopic}}
@hasDraft={{this.currentUser.has_topic_draft}}
- @createTopic={{route-action "createTopic"}}
+ @createTopic={{fn this.composer.openNewTopic (hash preferDraft=true)}}
@skipCategoriesNavItem={{this.skipCategoriesNavItem}}
/>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/tests/fixtures/session-fixtures.js b/app/assets/javascripts/discourse/tests/fixtures/session-fixtures.js
index 84e4d5077f9..cafba648596 100644
--- a/app/assets/javascripts/discourse/tests/fixtures/session-fixtures.js
+++ b/app/assets/javascripts/discourse/tests/fixtures/session-fixtures.js
@@ -14,6 +14,7 @@ export default {
moderator: true,
staff: true,
can_create_group: true,
+ can_create_topic: true,
title: "co-founder",
reply_count: 859,
topic_count: 36,
diff --git a/app/assets/javascripts/select-kit/addon/components/composer-actions.js b/app/assets/javascripts/select-kit/addon/components/composer-actions.js
index 20b515605e9..1c1081b53f1 100644
--- a/app/assets/javascripts/select-kit/addon/components/composer-actions.js
+++ b/app/assets/javascripts/select-kit/addon/components/composer-actions.js
@@ -13,6 +13,7 @@ import { camelize } from "@ember/string";
import { equal, gt } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
import { inject as service } from "@ember/service";
+import { escapeExpression } from "discourse/lib/utilities";
// Component can get destroyed and lose state
let _topicSnapshot = null;
@@ -27,6 +28,7 @@ export function _clearSnapshots() {
export default DropdownSelectBoxComponent.extend({
dialog: service(),
+ composer: service(),
seq: 0,
pluginApiIdentifiers: ["composer-actions"],
classNames: ["composer-actions"],
@@ -234,14 +236,32 @@ export default DropdownSelectBoxComponent.extend({
return items;
},
+ _continuedFromText(post, topic) {
+ let url = post?.url || topic?.url;
+ const topicTitle = topic?.title;
+
+ if (!url || !topicTitle) {
+ return;
+ }
+
+ url = `${location.protocol}//${location.host}${url}`;
+ const link = `[${escapeExpression(topicTitle)}](${url})`;
+ return I18n.t("post.continue_discussion", {
+ postLink: link,
+ });
+ },
+
_replyFromExisting(options, post, topic) {
- this.closeComposer();
- this.openComposer(options, post, topic);
+ this.composer.closeComposer();
+ this.composer.open({
+ ...options,
+ prependText: this._continuedFromText(post, topic),
+ });
},
_openComposer(options) {
- this.closeComposer();
- this.openComposer(options);
+ this.composer.closeComposer();
+ this.composer.open(options);
},
toggleWhisperSelected(options, model) {
diff --git a/spec/system/composer/default_to_subcategory_spec.rb b/spec/system/composer/default_to_subcategory_spec.rb
index c0ecc724264..1e2d43649d9 100644
--- a/spec/system/composer/default_to_subcategory_spec.rb
+++ b/spec/system/composer/default_to_subcategory_spec.rb
@@ -41,13 +41,9 @@ describe "Default to Subcategory when parent Category doesn't allow posting", ty
end
end
describe "Category does not have subcategory" do
- it "should have the 'New Topic' button enabled and default Subcategory set to latest default subcategory" do
+ it "should have the 'New Topic' button disabled" do
category_page.visit(category_with_no_subcategory)
- expect(category_page).to have_button("New Topic", disabled: false)
- category_page.new_topic_button.click
- select_kit =
- PageObjects::Components::SelectKit.new("#reply-control.open .category-chooser")
- expect(select_kit).to have_selected_value(default_latest_category.id)
+ expect(category_page).to have_button("New Topic", disabled: true)
end
end
end
@@ -70,13 +66,9 @@ describe "Default to Subcategory when parent Category doesn't allow posting", ty
end
describe "Can't post on parent category" do
describe "Category does not have subcategory" do
- it "should have the 'New Topic' button enabled and default Subcategory not set" do
+ it "should have the 'New Topic' button disabled" do
category_page.visit(category_with_no_subcategory)
- expect(category_page).to have_button("New Topic", disabled: false)
- category_page.new_topic_button.click
- select_kit =
- PageObjects::Components::SelectKit.new("#reply-control.open .category-chooser")
- expect(select_kit).to have_selected_name("category…")
+ expect(category_page).to have_button("New Topic", disabled: true)
end
end
end