FEATURE: Show all categories in composer (#13213)

FEATURE: Show all categories in composer (#13213)

…and just prioritize the current one, instead of hiding other categories.

Context: when you open the composer by clicking “New Topic” button when in a category, or by clicking “New Topic” in the share-popup, the category selector shows only the current category and its children (and “Uncategorized”). You can still find other categories, but you have to search by name. This PR changes that, so you now can see all the categories in the dropdown, and those that are relevant (again: current, children and uncategorized) are displayed before all other categories.

tldr: don’t make choosing other categories harder - make choosing relevant ones easier.

diff --git a/app/assets/javascripts/discourse/app/controllers/composer.js b/app/assets/javascripts/discourse/app/controllers/composer.js
index 5d1459d..e0c09b7 100644
--- a/app/assets/javascripts/discourse/app/controllers/composer.js
+++ b/app/assets/javascripts/discourse/app/controllers/composer.js
@@ -101,6 +101,7 @@ export default Controller.extend({
   showEditReason: false,
   editReason: null,
   scopedCategoryId: null,
+  prioritizedCategoryId: null,
   lastValidatedAt: null,
   isUploading: false,
   topic: null,
@@ -874,15 +875,22 @@ export default Controller.extend({
   },
 
   /**
-   Open the composer view
-
-   @method open
-   @param {Object} opts Options for creating a post
-   @param {String} opts.action The action we're performing: edit, reply or createTopic
-   @param {Post} [opts.post] The post we're replying to
-   @param {Topic} [opts.topic] The topic we're replying to
-   @param {String} [opts.quote] If we're opening a reply from a quote, the quote we're making
-   **/
+    Open the composer view
+
+    @method open
+    @param {Object} opts Options for creating a post
+      @param {String} opts.action The action we're performing: edit, reply, createTopic, createSharedDraft, privateMessage
+      @param {String} opts.draftKey
+      @param {Post} [opts.post] The post we're replying to
+      @param {Topic} [opts.topic] The topic we're replying to
+      @param {String} [opts.quote] If we're opening a reply from a quote, the quote we're making
+      @param {Boolean} [opts.ignoreIfChanged]
+      @param {Boolean} [opts.disableScopedCategory]
+      @param {Number} [opts.categoryId] Sets `scopedCategoryId` and `categoryId` on the Composer model
+      @param {Number} [opts.prioritizedCategoryId]
+      @param {String} [opts.draftSequence]
+      @param {Boolean} [opts.skipDraftCheck]
+  **/
   open(opts) {
     opts = opts || {};
 
@@ -904,6 +912,7 @@ export default Controller.extend({
       showEditReason: false,
       editReason: null,
       scopedCategoryId: null,
+      prioritizedCategoryId: null,
       skipAutoSave: true,
     });
 
@@ -915,6 +924,16 @@ export default Controller.extend({
       }
     }
 
+    if (opts.prioritizedCategoryId) {
+      const category = this.site.categories.findBy(
+        "id",
+        opts.prioritizedCategoryId
+      );
+      if (category) {
+        this.set("prioritizedCategoryId", opts.prioritizedCategoryId);
+      }
+    }
+
     // If we want a different draft than the current composer, close it and clear our model.
     if (
       composerModel &&
diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js
index c6a5289..c3e4060 100644
--- a/app/assets/javascripts/discourse/app/controllers/topic.js
+++ b/app/assets/javascripts/discourse/app/controllers/topic.js
@@ -1036,7 +1036,8 @@ export default Controller.extend(bufferedProperty("model"), {
         options = {
           action: Composer.CREATE_TOPIC,
           draftKey: post.topic.draft_key,
-          categoryId: this.get("model.category.id"),
+          topicCategoryId: this.get("model.category.id"),
+          prioritizedCategoryId: this.get("model.category.id"),
         };
       }
 
diff --git a/app/assets/javascripts/discourse/app/mixins/open-composer.js b/app/assets/javascripts/discourse/app/mixins/open-composer.js
index 8b21b28..2504698 100644
--- a/app/assets/javascripts/discourse/app/mixins/open-composer.js
+++ b/app/assets/javascripts/discourse/app/mixins/open-composer.js
@@ -14,7 +14,8 @@ export default Mixin.create({
     }
 
     this.controllerFor("composer").open({
-      categoryId,
+      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,
diff --git a/app/assets/javascripts/discourse/app/models/composer.js b/app/assets/javascripts/discourse/app/models/composer.js
index b36abfb..9851d48 100644
--- a/app/assets/javascripts/discourse/app/models/composer.js
+++ b/app/assets/javascripts/discourse/app/models/composer.js
@@ -693,15 +693,30 @@ const Composer = RestModel.extend({
     }
   },
 
-  /*
-     Open a composer
-
-     opts:
-       action   - The action we're performing: edit, reply or createTopic
-       post     - The post we're replying to, if present
-       topic    - The topic we're replying to, if present
-       quote    - If we're opening a reply from a quote, the quote we're making
-  */
+  /**
+    Open a composer
+
+    @method open
+    @param {Object} opts
+      @param {String} opts.action The action we're performing: edit, reply, createTopic, createSharedDraft, privateMessage
+      @param {String} opts.draftKey
+      @param {String} opts.draftSequence
+      @param {Post} [opts.post] The post we're replying to, if present
+      @param {Topic} [opts.topic] The topic we're replying to, if present
+      @param {String} [opts.quote] If we're opening a reply from a quote, the quote we're making
+      @param {String} [opts.reply]
+      @param {String} [opts.recipients]
+      @param {Number} [opts.composerTime]
+      @param {Number} [opts.typingTime]
+      @param {Boolean} [opts.whisper]
+      @param {Boolean} [opts.noBump]
+      @param {String} [opts.archetypeId] One of `site.archetypes` e.g. `regular` or `private_message`
+      @param {Object} [opts.metaData]
+      @param {Number} [opts.categoryId]
+      @param {Number} [opts.postId]
+      @param {Number} [opts.destinationCategoryId]
+      @param {String} [opts.title]
+  **/
   open(opts) {
     let promise = Promise.resolve();
 
diff --git a/app/assets/javascripts/discourse/app/templates/composer.hbs b/app/assets/javascripts/discourse/app/templates/composer.hbs
index d06cbcd..955df36 100644
--- a/app/assets/javascripts/discourse/app/templates/composer.hbs
+++ b/app/assets/javascripts/discourse/app/templates/composer.hbs
@@ -80,6 +80,7 @@
                       isDisabled=disableCategoryChooser
                       options=(hash
                         scopedCategoryId=scopedCategoryId
+                        prioritizedCategoryId=prioritizedCategoryId
                       )
                     }}
                     {{popup-input-tip validation=categoryValidation}}
diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js
index c695a35..b3e18a6 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js
@@ -89,6 +89,44 @@ discourseModule(
       },
     });
 
+    componentTest("with prioritizedCategoryId", {
+      template: hbs`
+        {{category-chooser
+          value=value
+          options=(hash
+            prioritizedCategoryId=5
+          )
+        }}
+      `,
+
+      async test(assert) {
+        await this.subject.expand();
+
+        // The prioritized category
+        assert.equal(this.subject.rowByIndex(0).value(), 5);
+        // The prioritized category's child
+        assert.equal(this.subject.rowByIndex(1).value(), 22);
+        // Other categories in the default order
+        assert.equal(this.subject.rowByIndex(2).value(), 6);
+        assert.equal(this.subject.rowByIndex(3).value(), 21);
+        assert.equal(this.subject.rowByIndex(4).value(), 1);
+
+        assert.equal(
+          this.subject.rows().length,
+          20,
+          "all categories are visible"
+        );
+
+        await this.subject.fillInFilter("bug");
+
+        assert.equal(
+          this.subject.rowByIndex(0).name(),
+          "bug",
+          "search still finds categories"
+        );
+      },
+    });
+

[... diff too long, it was truncated ...]

GitHub sha: 869518e3

This commit appears in #13213 which was approved by eviltrout. It was merged by CvX.