diff --git a/app/assets/javascripts/discourse/app/components/popup-input-tip.js b/app/assets/javascripts/discourse/app/components/popup-input-tip.js index 8508a7e7610..a119210a6e9 100644 --- a/app/assets/javascripts/discourse/app/components/popup-input-tip.js +++ b/app/assets/javascripts/discourse/app/components/popup-input-tip.js @@ -1,10 +1,11 @@ import { not, or, reads } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import Component from "@ember/component"; -import { getOwner } from "discourse-common/lib/get-owner"; import { htmlSafe } from "@ember/template"; +import { inject as service } from "@ember/service"; export default Component.extend({ + composer: service(), tagName: "a", classNameBindings: [":popup-tip", "good", "bad", "lastShownAt::hide"], attributeBindings: ["role", "ariaLabel", "tabindex"], @@ -28,8 +29,7 @@ export default Component.extend({ dismiss() { this.set("shownAt", null); - const composer = getOwner(this).lookup("controller:composer"); - composer.clearLastValidatedAt(); + this.composer.clearLastValidatedAt(); this.element.previousElementSibling?.focus(); }, diff --git a/app/assets/javascripts/discourse/app/components/sidebar/user/community-section.js b/app/assets/javascripts/discourse/app/components/sidebar/user/community-section.js index 2bcb92da48d..99964a082d9 100644 --- a/app/assets/javascripts/discourse/app/components/sidebar/user/community-section.js +++ b/app/assets/javascripts/discourse/app/components/sidebar/user/community-section.js @@ -16,8 +16,11 @@ import SidebarCommonCommunitySection from "discourse/components/sidebar/common/c import { action } from "@ember/object"; import { next } from "@ember/runloop"; +import { inject as service } from "@ember/service"; export default class SidebarUserCommunitySection extends SidebarCommonCommunitySection { + @service composer; + constructor() { super(...arguments); @@ -68,7 +71,7 @@ export default class SidebarUserCommunitySection extends SidebarCommonCommunityS } next(() => { - getOwner(this).lookup("controller:composer").open(composerArgs); + this.composer.open(composerArgs); }); } } diff --git a/app/assets/javascripts/discourse/app/components/user-stream.js b/app/assets/javascripts/discourse/app/components/user-stream.js index d025570b456..b6aa0dd839c 100644 --- a/app/assets/javascripts/discourse/app/components/user-stream.js +++ b/app/assets/javascripts/discourse/app/components/user-stream.js @@ -6,7 +6,6 @@ import I18n from "I18n"; import LoadMore from "discourse/mixins/load-more"; import Post from "discourse/models/post"; import { NEW_TOPIC_KEY } from "discourse/models/composer"; -import { getOwner } from "discourse-common/lib/get-owner"; import { observes } from "discourse-common/utils/decorators"; import { on } from "@ember/object/evented"; import { popupAjaxError } from "discourse/lib/ajax-error"; @@ -16,6 +15,7 @@ import { inject as service } from "@ember/service"; export default Component.extend(LoadMore, { tagName: "ul", dialog: service(), + composer: service(), _lastDecoratedElement: null, _initialize: on("init", function () { @@ -100,9 +100,8 @@ export default Component.extend(LoadMore, { }, resumeDraft(item) { - const composer = getOwner(this).lookup("controller:composer"); - if (composer.get("model.viewOpen")) { - composer.close(); + if (this.composer.get("model.viewOpen")) { + this.composer.close(); } if (item.get("postUrl")) { DiscourseURL.routeTo(item.get("postUrl")); @@ -114,7 +113,7 @@ export default Component.extend(LoadMore, { return; } - composer.open({ + this.composer.open({ draft, draftKey: item.draft_key, draftSequence: d.draft_sequence, diff --git a/app/assets/javascripts/discourse/app/controllers/full-page-search.js b/app/assets/javascripts/discourse/app/controllers/full-page-search.js index e27ce685fb8..519b9ff0669 100644 --- a/app/assets/javascripts/discourse/app/controllers/full-page-search.js +++ b/app/assets/javascripts/discourse/app/controllers/full-page-search.js @@ -22,6 +22,7 @@ import { Promise } from "rsvp"; import { search as searchCategoryTag } from "discourse/lib/category-tag-search"; import showModal from "discourse/lib/show-modal"; import userSearch from "discourse/lib/user-search"; +import { inject as service } from "@ember/service"; const SortOrders = [ { name: I18n.t("search.relevance"), id: 0 }, @@ -49,7 +50,7 @@ export function registerFullPageSearchType( export default Controller.extend({ application: controller(), - composer: controller(), + composer: service(), bulkSelectEnabled: null, loading: false, diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index 78e72ab5044..3a097c334ac 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -47,7 +47,7 @@ export function registerCustomPostMessageCallback(type, callback) { } export default Controller.extend(bufferedProperty("model"), { - composer: controller(), + composer: service(), application: controller(), dialog: service(), documentTitle: service(), diff --git a/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js b/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js index e5b75854665..909d0ec1d11 100644 --- a/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js +++ b/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js @@ -445,14 +445,14 @@ export default { return; } - this.container.lookup("controller:composer").open({ + this.container.lookup("service:composer").open({ action: Composer.CREATE_TOPIC, draftKey: Composer.NEW_TOPIC_KEY, }); }, focusComposer(event) { - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); if (event) { event.preventDefault(); event.stopPropagation(); @@ -461,7 +461,7 @@ export default { }, fullscreenComposer() { - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); if (composer.get("model")) { composer.toggleFullscreen(); } diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js index ed2ca159f1b..9d5c128de78 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.js +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js @@ -58,7 +58,7 @@ import { addPluginReviewableParam } from "discourse/components/reviewable-item"; import { addComposerSaveErrorCallback, addPopupMenuOptionsCallback, -} from "discourse/controllers/composer"; +} from "discourse/services/composer"; import { addPostClassesCallback } from "discourse/widgets/post"; import { addGroupPostSmallActionCode, diff --git a/app/assets/javascripts/discourse/app/lib/url.js b/app/assets/javascripts/discourse/app/lib/url.js index f7d7eb89bd7..b2a4046388a 100644 --- a/app/assets/javascripts/discourse/app/lib/url.js +++ b/app/assets/javascripts/discourse/app/lib/url.js @@ -409,7 +409,7 @@ const DiscourseURL = EmberObject.extend({ }, get isComposerOpen() { - return this.controllerFor("composer")?.visible; + return this.container.lookup("service:composer")?.visible; }, get router() { diff --git a/app/assets/javascripts/discourse/app/mixins/open-composer.js b/app/assets/javascripts/discourse/app/mixins/open-composer.js index c5e58c52ad4..1e06c9cf396 100644 --- a/app/assets/javascripts/discourse/app/mixins/open-composer.js +++ b/app/assets/javascripts/discourse/app/mixins/open-composer.js @@ -1,6 +1,7 @@ // 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) { @@ -13,13 +14,15 @@ export default Mixin.create({ categoryId = null; } - this.controllerFor("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, - }); + 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( @@ -29,15 +32,17 @@ export default Mixin.create({ topicCategoryId, topicTags ) { - this.controllerFor("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"), - }); + 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({ @@ -46,7 +51,7 @@ export default Mixin.create({ topicBody = "", hasGroups = false, } = {}) { - this.controllerFor("composer").open({ + getOwner(this).lookup("service:composer").open({ action: Composer.PRIVATE_MESSAGE, recipients, topicTitle, diff --git a/app/assets/javascripts/discourse/app/routes/application.js b/app/assets/javascripts/discourse/app/routes/application.js index 0f612eeb46e..317efa6ca52 100644 --- a/app/assets/javascripts/discourse/app/routes/application.js +++ b/app/assets/javascripts/discourse/app/routes/application.js @@ -39,6 +39,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, { shortSiteDescription: setting("short_site_description"), documentTitle: service(), dialog: service(), + composer: service(), actions: { toggleAnonymous() { @@ -84,7 +85,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, { : null; // used only once, one less dependency - return this.controllerFor("composer").open({ + return this.composer.open({ action: Composer.PRIVATE_MESSAGE, recipients, archetypeId: "private_message", 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 df3061caa42..9134bc71947 100644 --- a/app/assets/javascripts/discourse/app/routes/build-category-route.js +++ b/app/assets/javascripts/discourse/app/routes/build-category-route.js @@ -16,11 +16,13 @@ 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"; +import { inject as service } from "@ember/service"; // A helper function to create a category route with parameters export default (filterArg, params) => { return DiscourseRoute.extend({ queryParams, + composer: service(), model(modelParams) { const category = Category.findBySlugPathWithID( @@ -209,7 +211,7 @@ export default (filterArg, params) => { deactivate() { this._super(...arguments); - this.controllerFor("composer").set("prioritizedCategoryId", null); + this.composer.set("prioritizedCategoryId", null); this.searchService.set("searchContext", null); }, diff --git a/app/assets/javascripts/discourse/app/routes/discourse.js b/app/assets/javascripts/discourse/app/routes/discourse.js index fc9792da024..9b2f976c5c7 100644 --- a/app/assets/javascripts/discourse/app/routes/discourse.js +++ b/app/assets/javascripts/discourse/app/routes/discourse.js @@ -3,6 +3,7 @@ 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"; const DiscourseRoute = Route.extend({ showFooter: false, @@ -53,7 +54,7 @@ const DiscourseRoute = Route.extend({ }, openTopicDraft() { - const composer = this.controllerFor("composer"); + const composer = getOwner(this).lookup("service:composer"); if ( composer.get("model.action") === Composer.CREATE_TOPIC && diff --git a/app/assets/javascripts/discourse/app/routes/tag-show.js b/app/assets/javascripts/discourse/app/routes/tag-show.js index dfdcf3aa29d..8d3d00eb13c 100644 --- a/app/assets/javascripts/discourse/app/routes/tag-show.js +++ b/app/assets/javascripts/discourse/app/routes/tag-show.js @@ -18,11 +18,13 @@ import { setTopicList } from "discourse/lib/topic-list-tracker"; import showModal from "discourse/lib/show-modal"; import { action } from "@ember/object"; import PreloadStore from "discourse/lib/preload-store"; +import { inject as service } from "@ember/service"; const NONE = "none"; const ALL = "all"; export default DiscourseRoute.extend(FilterModeMixin, { + composer: service(), navMode: "latest", queryParams, @@ -214,8 +216,7 @@ export default DiscourseRoute.extend(FilterModeMixin, { this.openTopicDraft(); } else { const controller = this.controllerFor("tag.show"); - const composerController = this.controllerFor("composer"); - composerController + this.composer .open({ categoryId: controller.category?.id, action: Composer.CREATE_TOPIC, @@ -223,8 +224,8 @@ export default DiscourseRoute.extend(FilterModeMixin, { }) .then(() => { // Pre-fill the tags input field - if (composerController.canEditTags && controller.tag?.id) { - const composerModel = this.controllerFor("composer").model; + if (this.composer.canEditTags && controller.tag?.id) { + const composerModel = this.composer.model; composerModel.set("tags", this._controllerTags(controller)); } }); diff --git a/app/assets/javascripts/discourse/app/routes/topic-from-params.js b/app/assets/javascripts/discourse/app/routes/topic-from-params.js index 5875089d6d7..2f5f6334d84 100644 --- a/app/assets/javascripts/discourse/app/routes/topic-from-params.js +++ b/app/assets/javascripts/discourse/app/routes/topic-from-params.js @@ -5,9 +5,12 @@ import { isEmpty } from "@ember/utils"; import { isTesting } from "discourse-common/config/environment"; import { schedule } from "@ember/runloop"; import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; // This route is used for retrieving a topic based on params export default DiscourseRoute.extend({ + composer: service(), + // Avoid default model hook model(params) { params = params || {}; @@ -56,7 +59,6 @@ export default DiscourseRoute.extend({ } const topicController = this.controllerFor("topic"); - const composerController = this.controllerFor("composer"); const topic = this.modelFor("topic"); const postStream = topic.postStream; @@ -105,7 +107,7 @@ export default DiscourseRoute.extend({ } if (!isEmpty(topic.draft)) { - composerController.open({ + this.composer.open({ draft: Draft.getLocal(topic.draft_key, topic.draft), draftKey: topic.draft_key, draftSequence: topic.draft_sequence, diff --git a/app/assets/javascripts/discourse/app/routes/topic.js b/app/assets/javascripts/discourse/app/routes/topic.js index fcb14b14673..861ad01e0f9 100644 --- a/app/assets/javascripts/discourse/app/routes/topic.js +++ b/app/assets/javascripts/discourse/app/routes/topic.js @@ -14,6 +14,7 @@ import PostFlag from "discourse/lib/flag-targets/post-flag"; const SCROLL_DELAY = 500; const TopicRoute = DiscourseRoute.extend({ + composer: service(), screenTrack: service(), scheduledReplace: null, @@ -332,7 +333,7 @@ const TopicRoute = DiscourseRoute.extend({ postStream.cancelFilter(); topicController.set("multiSelect", false); - this.controllerFor("composer").set("topic", null); + this.composer.set("topic", null); this.screenTrack.stop(); this.appEvents.trigger("header:hide-topic"); @@ -356,7 +357,7 @@ const TopicRoute = DiscourseRoute.extend({ controller.set("multiSelect", false); controller.get("quoteState").clear(); - this.controllerFor("composer").set("topic", model); + this.composer.set("topic", model); this.topicTrackingState.trackIncoming("all"); // We reset screen tracking every time a topic is entered diff --git a/app/assets/javascripts/discourse/app/routes/user-private-messages.js b/app/assets/javascripts/discourse/app/routes/user-private-messages.js index 319cc1caad8..5e631ce9ac8 100644 --- a/app/assets/javascripts/discourse/app/routes/user-private-messages.js +++ b/app/assets/javascripts/discourse/app/routes/user-private-messages.js @@ -2,8 +2,11 @@ import Composer from "discourse/models/composer"; import DiscourseRoute from "discourse/routes/discourse"; import Draft from "discourse/models/draft"; import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; export default DiscourseRoute.extend({ + composer: service(), + renderTemplate() { this.render("user/messages"); }, @@ -20,11 +23,9 @@ export default DiscourseRoute.extend({ controller.set("model", model); if (this.currentUser) { - const composerController = this.controllerFor("composer"); - Draft.get("new_private_message").then((data) => { if (data.draft) { - composerController.open({ + this.composer.open({ draft: data.draft, draftKey: Composer.NEW_PRIVATE_MESSAGE_KEY, ignoreIfChanged: true, diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js index dcd0adea595..3e126039f9b 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js @@ -12,7 +12,7 @@ import I18n from "I18n"; import selectKit from "discourse/tests/helpers/select-kit-helper"; import sinon from "sinon"; import { test } from "qunit"; -import { toggleCheckDraftPopup } from "discourse/controllers/composer"; +import { toggleCheckDraftPopup } from "discourse/services/composer"; import userFixtures from "discourse/tests/fixtures/user-fixtures"; import { cloneJSON } from "discourse-common/lib/object"; diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js index d021bd1c9e5..0638c39aae4 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js @@ -7,7 +7,7 @@ import { triggerKeyEvent, visit, } from "@ember/test-helpers"; -import { toggleCheckDraftPopup } from "discourse/controllers/composer"; +import { toggleCheckDraftPopup } from "discourse/services/composer"; import { cloneJSON } from "discourse-common/lib/object"; import TopicFixtures from "discourse/tests/fixtures/topic"; import LinkLookup from "discourse/lib/link-lookup"; @@ -1016,7 +1016,7 @@ acceptance("Composer", function (needs) { await visit("/t/internationalization-localization/280"); await click("#topic-footer-buttons .create"); - this.container.lookup("controller:composer").set( + this.container.lookup("service:composer").set( "linkLookup", new LinkLookup({ "github.com": { @@ -1185,7 +1185,7 @@ acceptance("Composer - Focus Open and Closed", function (needs) { test("Focusing a composer which is not open with create topic", async function (assert) { await visit("/t/internationalization-localization/280"); - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); await composer.focusComposer({ fallbackToNewTopic: true }); await settled(); @@ -1200,7 +1200,7 @@ acceptance("Composer - Focus Open and Closed", function (needs) { test("Focusing a composer which is not open with create topic and append text", async function (assert) { await visit("/t/internationalization-localization/280"); - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); await composer.focusComposer({ fallbackToNewTopic: true, insertText: "this is appended", @@ -1222,7 +1222,7 @@ acceptance("Composer - Focus Open and Closed", function (needs) { await visit("/"); await click("#create-topic"); - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); await composer.focusComposer(); await settled(); @@ -1237,7 +1237,7 @@ acceptance("Composer - Focus Open and Closed", function (needs) { await visit("/"); await click("#create-topic"); - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); await composer.focusComposer({ insertText: "this is some appended text" }); await settled(); @@ -1259,7 +1259,7 @@ acceptance("Composer - Focus Open and Closed", function (needs) { await fillIn(".d-editor-input", "This is a dirty reply"); await click(".toggle-minimize"); - const composer = this.container.lookup("controller:composer"); + const composer = this.container.lookup("service:composer"); await composer.focusComposer({ insertText: "this is some appended text" }); await settled(); diff --git a/app/assets/javascripts/discourse/tests/acceptance/tags-test.js b/app/assets/javascripts/discourse/tests/acceptance/tags-test.js index cfeb1b29446..f7db49b9f4b 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/tags-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/tags-test.js @@ -542,7 +542,7 @@ acceptance("Tag info", function (needs) { test("composer will not set tags if user cannot create them", async function (assert) { await visit("/tag/planters"); await click("#create-topic"); - let composer = this.owner.lookup("controller:composer"); + let composer = this.owner.lookup("service:composer"); assert.strictEqual(composer.get("model").tags, undefined); }); }); @@ -611,7 +611,7 @@ acceptance("Tag show - create topic", function (needs) { }); test("composer will not set tags with all/none tags when creating topic", async function (assert) { - const composer = this.owner.lookup("controller:composer"); + const composer = this.owner.lookup("service:composer"); await visit("/tag/none"); await click("#create-topic"); @@ -623,7 +623,7 @@ acceptance("Tag show - create topic", function (needs) { }); test("composer will set tags from selected tag", async function (assert) { - const composer = this.owner.lookup("controller:composer"); + const composer = this.owner.lookup("service:composer"); await visit("/tag/planters"); await click("#create-topic");