FEATURE: user/category/tag results in full page search (#14346)

FEATURE: user/category/tag results in full page search (#14346)

See PR for details, this commit also changes the layout of the full page search.

diff --git a/app/assets/javascripts/discourse/app/components/search-advanced-options.js b/app/assets/javascripts/discourse/app/components/search-advanced-options.js
index 0bcb500..60658c9 100644
--- a/app/assets/javascripts/discourse/app/components/search-advanced-options.js
+++ b/app/assets/javascripts/discourse/app/components/search-advanced-options.js
@@ -80,7 +80,9 @@ export function addAdvancedSearchOptions(options) {
 }
 
 export default Component.extend({
-  classNames: ["search-advanced-options"],
+  tagName: "details",
+  attributeBindings: ["expandFilters:open"],
+  classNames: ["advanced-filters"],
   category: null,
 
   init() {
@@ -116,6 +118,7 @@ export default Component.extend({
         : inOptionsForAll(),
       statusOptions: statusOptions(),
       postTimeOptions: postTimeOptions(),
+      showAllTagsCheckbox: false,
     });
   },
 
@@ -313,10 +316,10 @@ export default Component.extend({
       const userInput = match[0].replace(REGEXP_TAGS_REPLACE, "");
 
       if (existingInput !== userInput) {
-        this.set(
-          "searchedTerms.tags",
-          userInput.length !== 0 ? userInput.split(joinChar) : null
-        );
+        const updatedTags = userInput?.split(joinChar);
+
+        this.set("searchedTerms.tags", updatedTags);
+        this.set("showAllTagsCheckbox", !!(updatedTags.length > 1));
       }
     } else if (!tags) {
       this.set("searchedTerms.tags", null);
@@ -496,6 +499,9 @@ export default Component.extend({
         searchTerm += ` tags:${tags}`;
       }
 
+      if (tagFilter.length > 1) {
+        this.set("showAllTagsCheckbox", true);
+      }
       this._updateSearchTerm(searchTerm);
     } else if (match.length !== 0) {
       searchTerm = searchTerm.replace(match[0], "");
diff --git a/app/assets/javascripts/discourse/app/components/search-result-entry.js b/app/assets/javascripts/discourse/app/components/search-result-entry.js
index 4449440..59cd222 100644
--- a/app/assets/javascripts/discourse/app/components/search-result-entry.js
+++ b/app/assets/javascripts/discourse/app/components/search-result-entry.js
@@ -1,5 +1,7 @@
 import Component from "@ember/component";
 
 export default Component.extend({
-  tagName: "",
+  tagName: "div",
+  classNames: ["fps-result"],
+  classNameBindings: ["bulkSelectEnabled"],
 });
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 4fdd54a..a5bbf30 100644
--- a/app/assets/javascripts/discourse/app/controllers/full-page-search.js
+++ b/app/assets/javascripts/discourse/app/controllers/full-page-search.js
@@ -15,6 +15,9 @@ import { isEmpty } from "@ember/utils";
 import { or } from "@ember/object/computed";
 import { scrollTop } from "discourse/mixins/scroll-top";
 import { setTransient } from "discourse/lib/page-tracker";
+import { Promise } from "rsvp";
+import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
+import userSearch from "discourse/lib/user-search";
 
 const SortOrders = [
   { name: I18n.t("search.relevance"), id: 0 },
@@ -23,6 +26,19 @@ const SortOrders = [
   { name: I18n.t("search.most_viewed"), id: 3, term: "order:views" },
   { name: I18n.t("search.latest_topic"), id: 4, term: "order:latest_topic" },
 ];
+
+export const SEARCH_TYPE_DEFAULT = "topics_posts";
+export const SEARCH_TYPE_CATS_TAGS = "categories_tags";
+export const SEARCH_TYPE_USERS = "users";
+
+const SearchTypes = [
+  { name: I18n.t("search.type.default"), id: SEARCH_TYPE_DEFAULT },
+  {
+    name: I18n.t("search.type.categories_and_tags"),
+    id: SEARCH_TYPE_CATS_TAGS,
+  },
+  { name: I18n.t("search.type.users"), id: SEARCH_TYPE_USERS },
+];
 const PAGE_LIMIT = 10;
 
 export default Controller.extend({
@@ -31,11 +47,17 @@ export default Controller.extend({
   bulkSelectEnabled: null,
 
   loading: false,
-  queryParams: ["q", "expanded", "context_id", "context", "skip_context"],
-  q: null,
-  selected: [],
-  expanded: false,
+  queryParams: [
+    "q",
+    "expanded",
+    "context_id",
+    "context",
+    "skip_context",
+    "search_type",
+  ],
+  q: undefined,
   context_id: null,
+  search_type: SEARCH_TYPE_DEFAULT,
   context: null,
   searching: false,
   sortOrder: 0,
@@ -43,12 +65,24 @@ export default Controller.extend({
   invalidSearch: false,
   page: 1,
   resultCount: null,
+  searchTypes: SearchTypes,
+
+  init() {
+    this._super(...arguments);
+
+    this.selected = [];
+  },
 
   @discourseComputed("resultCount")
   hasResults(resultCount) {
     return (resultCount || 0) > 0;
   },
 
+  @discourseComputed("expanded")
+  expandFilters(expanded) {
+    return expanded === "true";
+  },
+
   @discourseComputed("q")
   hasAutofocus(q) {
     return isEmpty(q);
@@ -138,6 +172,14 @@ export default Controller.extend({
     }
   },
 
+  @observes("search_type")
+  triggerSearchOnTypeChange() {
+    if (this.searchActive) {
+      this.set("page", 1);
+      this._search();
+    }
+  },
+
   @observes("model")
   modelChanged() {
     if (this.searchTerm !== this.q) {
@@ -182,9 +224,19 @@ export default Controller.extend({
     return I18n.t("search.result_count", { count, plus, term });
   },
 
-  @observes("model.posts.length")
+  @observes("model.[posts,categories,tags,users].length")
   resultCountChanged() {
-    this.set("resultCount", this.get("model.posts.length"));
+    if (!this.model.posts) {
+      return 0;
+    }
+
+    this.set(
+      "resultCount",
+      this.model.posts.length +
+        this.model.categories.length +
+        this.model.tags.length +
+        this.model.users.length
+    );
   },
 
   @discourseComputed("hasResults")
@@ -202,6 +254,18 @@ export default Controller.extend({
     return page === PAGE_LIMIT;
   },
 
+  @discourseComputed("search_type")
+  usingDefaultSearchType(searchType) {
+    return searchType === SEARCH_TYPE_DEFAULT;
+  },
+
+  @discourseComputed("bulkSelectEnabled")
+  searchInfoClassNames(bulkSelectEnabled) {
+    return bulkSelectEnabled
+      ? "search-info bulk-select-visible"
+      : "search-info";
+  },
+
   searchButtonDisabled: or("searching", "loading"),
 
   _search() {
@@ -244,33 +308,71 @@ export default Controller.extend({
 
     const searchKey = getSearchKey(args);
 
-    ajax("/search", { data: args })
-      .then(async (results) => {
-        const model = (await translateResults(results)) || {};
-
-        if (results.grouped_search_result) {
-          this.set("q", results.grouped_search_result.term);
-        }
-
-        if (args.page > 1) {
-          if (model) {
-            this.model.posts.pushObjects(model.posts);
-            this.model.topics.pushObjects(model.topics);
-            this.model.set(
-              "grouped_search_result",
-              results.grouped_search_result
-            );
-          }
-        } else {
-          setTransient("lastSearch", { searchKey, model }, 5);
-          model.grouped_search_result = results.grouped_search_result;
-          this.set("model", model);
-        }
-      })
-      .finally(() => {
-        this.set("searching", false);
-        this.set("loading", false);
-      });
+    switch (this.search_type) {
+      case SEARCH_TYPE_CATS_TAGS:
+        const categoryTagSearch = searchCategoryTag(
+          searchTerm,
+          this.siteSettings
+        );
+        Promise.resolve(categoryTagSearch)
+          .then(async (results) => {
+            const categories = results.filter((c) => Boolean(c.model));
+            const tags = results.filter((c) => !Boolean(c.model));
+            const model = (await translateResults({ categories, tags })) || {};
+            this.set("model", model);
+          })
+          .finally(() => {
+            this.setProperties({
+              searching: false,
+              loading: false,
+            });
+          });
+        break;
+      case SEARCH_TYPE_USERS:
+        userSearch({ term: searchTerm, limit: 20 })

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

GitHub sha: dfeca42bf82b07c054bca940da986318fd91c02a

This commit appears in #14346 which was approved by eviltrout. It was merged by pmusaraj.