DEV: Remove OpenComposer mixin and refactor related logic (#23015)

The OpenComposer mixin comes from a time before we had a composer service. As well as being a general cleanup/refactor, this commit aims to removes interlinking between composer APIs and the discovery-related controllers which are being removed as part of #22622.

In summary, this commit:
- Removes OpenComposer mixin
- Adds and updates composer service APIs to support everything that `openComposer` did
- Updates consumers to call the composer service directly, instead of relying on the mixin (either directly, or via a route-action which bubbled up to some parent)
- Deprecates composer-related methods on `DiscourseRoute` and on the application route
This commit is contained in:
David Taylor 2023-08-11 09:53:44 +01:00 committed by GitHub
parent 31626ce85d
commit 6de4b3ac3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 253 additions and 293 deletions

View File

@ -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"}}
/>

View File

@ -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}}

View File

@ -46,8 +46,6 @@
<div class="reply-details">
<ComposerActionTitle
@model={{this.composer.model}}
@openComposer={{this.composer.openComposer}}
@closeComposer={{this.composer.closeComposer}}
@canWhisper={{this.composer.canWhisper}}
/>

View File

@ -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,
});

View File

@ -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;

View File

@ -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,
});

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -7,6 +7,7 @@ import { tracked } from "@glimmer/tracking";
export default class NavigationDefaultController extends Controller {
@service router;
@service composer;
@controller discovery;
@tracked category;

View File

@ -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,
});
},
});

View File

@ -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;

View File

@ -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,
});
},

View File

@ -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");

View File

@ -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 });
}
},

View File

@ -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));

View File

@ -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";

View File

@ -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,
});
}
});

View File

@ -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;
},
});
}
}

View File

@ -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);

View File

@ -3,7 +3,7 @@
{{#unless this.viewingCategoriesList}}
<CategoryReadOnlyBanner
@category={{this.category}}
@readOnly={{this.navigationCategory.cannotCreateTopicOnCategory}}
@readOnly={{not this.navigationCategory.enableCreateTopicButton}}
/>
{{/unless}}
</div>

View File

@ -119,9 +119,12 @@
@message={{this.footerMessage}}
>
{{#if this.latest}}
{{#if this.canCreateTopicOnCategory}}
{{#if this.category.canCreateTopic}}
<DiscourseLinkedText
@action={{route-action "createTopic"}}
@action={{fn
this.composer.openNewTopic
(hash category=this.category preferDraft=true)
}}
@text="topic.suggest_create_topic"
/>
{{/if}}

View File

@ -76,9 +76,12 @@
@message={{this.footerMessage}}
>
{{#if this.latest}}
{{#if this.canCreateTopicOnCategory}}
{{#if this.category.canCreateTopic}}
<DiscourseLinkedText
@action={{route-action "createTopic"}}
@action={{fn
this.composer.openNewTopic
(hash category=this.category preferDraft=true)
}}
@text="topic.suggest_create_topic"
/>
{{/if}}

View File

@ -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)}}
/>
</DSection>

View File

@ -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}}
/>

View File

@ -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}}
/>
</DSection>

View File

@ -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,

View File

@ -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) {

View File

@ -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&hellip;")
expect(category_page).to have_button("New Topic", disabled: true)
end
end
end