FEATURE: add expandable muted categories ui to `/categories` page. (#10303)

FEATURE: add expandable muted categories ui to /categories page. (#10303)

Co-authored-by: Joffrey JAFFEUX j.jaffeux@gmail.com

diff --git a/app/assets/javascripts/discourse/app/components/categories-only.js b/app/assets/javascripts/discourse/app/components/categories-only.js
index d218d69..f797eb3 100644
--- a/app/assets/javascripts/discourse/app/components/categories-only.js
+++ b/app/assets/javascripts/discourse/app/components/categories-only.js
@@ -1,7 +1,54 @@
 import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
 import { equal } from "@ember/object/computed";
+import { action } from "@ember/object";
 
 export default Component.extend({
   tagName: "",
-  noCategoryStyle: equal("siteSettings.category_style", "none")
+  showMuted: false,
+  noCategoryStyle: equal("siteSettings.category_style", "none"),
+
+  @discourseComputed("showMutedCategories", "filteredCategories.length")
+  mutedToggleIcon(showMutedCategories, filteredCategoriesLength) {
+    if (filteredCategoriesLength === 0) {
+      return;
+    }
+
+    if (showMutedCategories) return "minus";
+
+    return "plus";
+  },
+
+  @discourseComputed("showMuted", "filteredCategories.length")
+  showMutedCategories(showMuted, filteredCategoriesLength) {
+    return showMuted || filteredCategoriesLength === 0;
+  },
+
+  @discourseComputed("categories", "categories.length")
+  filteredCategories(categories, categoriesLength) {
+    if (!categories || categoriesLength === 0) {
+      return [];
+    }
+
+    return categories.filter(cat => !cat.isHidden);
+  },
+
+  @discourseComputed("categories", "categories.length")
+  mutedCategories(categories, categoriesLength) {
+    if (!categories || categoriesLength === 0) {
+      return [];
+    }
+
+    // hide in single category pages
+    if (categories.firstObject.parent_category_id) {
+      return [];
+    }
+
+    return categories.filterBy("hasMuted");
+  },
+
+  @action
+  toggleShowMuted() {
+    this.toggleProperty("showMuted");
+  }
 });
diff --git a/app/assets/javascripts/discourse/app/components/category-list-item.js b/app/assets/javascripts/discourse/app/components/category-list-item.js
new file mode 100644
index 0000000..e981a59
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/category-list-item.js
@@ -0,0 +1,29 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+
+const LIST_TYPE = {
+  NORMAL: "normal",
+  MUTED: "muted"
+};
+
+export default Component.extend({
+  tagName: "",
+  category: null,
+  listType: LIST_TYPE.NORMAL,
+
+  @discourseComputed("category.isHidden", "category.hasMuted", "listType")
+  isHidden(isHiddenCategory, hasMuted, listType) {
+    return (
+      (isHiddenCategory && listType === LIST_TYPE.NORMAL) ||
+      (!hasMuted && listType === LIST_TYPE.MUTED)
+    );
+  },
+
+  @discourseComputed("category.isMuted", "listType")
+  isMuted(isMutedCategory, listType) {
+    return (
+      (isMutedCategory && listType === LIST_TYPE.NORMAL) ||
+      (!isMutedCategory && listType === LIST_TYPE.MUTED)
+    );
+  }
+});
diff --git a/app/assets/javascripts/discourse/app/components/parent-category-row.js b/app/assets/javascripts/discourse/app/components/parent-category-row.js
new file mode 100644
index 0000000..5d22a61
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/parent-category-row.js
@@ -0,0 +1,3 @@
+import CategoryListItem from "discourse/components/category-list-item";
+
+export default CategoryListItem.extend({});
diff --git a/app/assets/javascripts/discourse/app/components/sub-category-item.js b/app/assets/javascripts/discourse/app/components/sub-category-item.js
new file mode 100644
index 0000000..5d22a61
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/sub-category-item.js
@@ -0,0 +1,3 @@
+import CategoryListItem from "discourse/components/category-list-item";
+
+export default CategoryListItem.extend({});
diff --git a/app/assets/javascripts/discourse/app/components/sub-category-row.js b/app/assets/javascripts/discourse/app/components/sub-category-row.js
new file mode 100644
index 0000000..5d22a61
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/sub-category-row.js
@@ -0,0 +1,3 @@
+import CategoryListItem from "discourse/components/category-list-item";
+
+export default CategoryListItem.extend({});
diff --git a/app/assets/javascripts/discourse/app/models/category.js b/app/assets/javascripts/discourse/app/models/category.js
index b016728..049a1bb 100644
--- a/app/assets/javascripts/discourse/app/models/category.js
+++ b/app/assets/javascripts/discourse/app/models/category.js
@@ -87,6 +87,36 @@ const Category = RestModel.extend({
     return notificationLevel === NotificationLevels.MUTED;
   },
 
+  @discourseComputed("isMuted", "subcategories")
+  isHidden(isMuted, subcategories) {
+    if (!isMuted) {
+      return false;
+    } else if (!subcategories) {
+      return true;
+    }
+
+    if (subcategories.some(cat => !cat.isHidden)) {
+      return false;
+    }
+
+    return true;
+  },
+
+  @discourseComputed("isMuted", "subcategories")
+  hasMuted(isMuted, subcategories) {
+    if (isMuted) {
+      return true;
+    } else if (!subcategories) {
+      return false;
+    }
+
+    if (subcategories.some(cat => cat.hasMuted)) {
+      return true;
+    }
+
+    return false;
+  },
+
   @discourseComputed("notification_level")
   notificationLevelString(notificationLevel) {
     // Get the key from the value
diff --git a/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs b/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs
index e030adc..6784ddf 100644
--- a/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs
@@ -1,91 +1,49 @@
 {{#if categories}}
-  <table class="category-list {{if showTopics "with-topics"}}">
-    <thead>
-      <tr>
-        <th class="category"><span aria-role="heading" aria-level="2" id="categories-only-category">{{i18n "categories.category"}}</span></th>
-        <th class="topics">{{i18n "categories.topics"}}</th>
-        {{#if showTopics}}
-          <th class="latest">{{i18n "categories.latest"}}</th>
+  {{#if filteredCategories}}
+    <table class="category-list {{if showTopics "with-topics"}}">
+      <thead>
+        <tr>
+          <th class="category"><span aria-role="heading" aria-level="2" id="categories-only-category">{{i18n "categories.category"}}</span></th>
+          <th class="topics">{{i18n "categories.topics"}}</th>
+          {{#if showTopics}}
+            <th class="latest">{{i18n "categories.latest"}}</th>
+          {{/if}}
+        </tr>
+      </thead>
+      <tbody aria-labelledby="categories-only-category">
+        {{#each categories as |category|}}
+          {{parent-category-row category=category showTopics=showTopics}}
+        {{/each}}
+      </tbody>
+    </table>
+  {{/if}}
+
+  {{#if mutedCategories}}
+    <div class="muted-categories">
+      <a href class="muted-categories-link" {{action "toggleShowMuted"}}>
+        <h3 class="muted-categories-heading">{{i18n "categories.muted"}}</h3>
+        {{#if mutedToggleIcon}}
+          {{d-icon mutedToggleIcon}}
         {{/if}}
-      </tr>
-    </thead>
-    <tbody aria-labelledby="categories-only-category">
-      {{#each categories as |c|}}
-        {{plugin-outlet name="category-list-above-each-category" connectorTagName="" tagName="" args=(hash category=c)}}
-        <tr data-category-id={{c.id}} data-notification-level={{c.notificationLevelString}} class="{{if c.description_excerpt "has-description" "no-description"}} {{if c.uploaded_logo.url "has-logo" "no-logo"}}">
-          <td class="category {{if c.isMuted "muted"}} {{if noCategoryStyle "no-category-style"}}" style={{unless noCategoryStyle (border-color c.color)}}>
-            {{category-title-link category=c}}
-            {{#unless c.isMuted}}
-              {{#if c.description_excerpt}}

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

GitHub sha: 6a1746a0

This commit appears in #10303 which was merged by vinothkannans.