mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Remove widget search menu (#25545)
This commit is contained in:
parent
2caaadea4a
commit
5c43fd5054
@ -17,7 +17,7 @@ import {
|
|||||||
registerReviewableActionModal,
|
registerReviewableActionModal,
|
||||||
} from "discourse/components/reviewable-item";
|
} from "discourse/components/reviewable-item";
|
||||||
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
||||||
import { addSearchSuggestion as addGlimmerSearchSuggestion } from "discourse/components/search-menu/results/assistant";
|
import { addSearchSuggestion } from "discourse/components/search-menu/results/assistant";
|
||||||
import { addItemSelectCallback as addSearchMenuAssistantSelectCallback } from "discourse/components/search-menu/results/assistant-item";
|
import { addItemSelectCallback as addSearchMenuAssistantSelectCallback } from "discourse/components/search-menu/results/assistant-item";
|
||||||
import {
|
import {
|
||||||
addQuickSearchRandomTip,
|
addQuickSearchRandomTip,
|
||||||
@ -117,12 +117,6 @@ import {
|
|||||||
preventCloak,
|
preventCloak,
|
||||||
} from "discourse/widgets/post-stream";
|
} from "discourse/widgets/post-stream";
|
||||||
import { disableNameSuppression } from "discourse/widgets/poster-name";
|
import { disableNameSuppression } from "discourse/widgets/poster-name";
|
||||||
import { addOnKeyDownCallback } from "discourse/widgets/search-menu";
|
|
||||||
import {
|
|
||||||
addQuickSearchRandomTip as addWidgetQuickSearchRandomTip,
|
|
||||||
addSearchSuggestion,
|
|
||||||
removeDefaultQuickSearchRandomTips as removeWidgetDefaultQuickSearchRandomTips,
|
|
||||||
} from "discourse/widgets/search-menu-results";
|
|
||||||
import {
|
import {
|
||||||
changeSetting,
|
changeSetting,
|
||||||
createWidget,
|
createWidget,
|
||||||
@ -1867,7 +1861,6 @@ class PluginApi {
|
|||||||
*/
|
*/
|
||||||
addSearchSuggestion(value) {
|
addSearchSuggestion(value) {
|
||||||
addSearchSuggestion(value);
|
addSearchSuggestion(value);
|
||||||
addGlimmerSearchSuggestion(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1941,7 +1934,6 @@ class PluginApi {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
addSearchMenuOnKeyDownCallback(fn) {
|
addSearchMenuOnKeyDownCallback(fn) {
|
||||||
addOnKeyDownCallback(fn);
|
|
||||||
addOnKeyUpCallback(fn);
|
addOnKeyUpCallback(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1962,7 +1954,6 @@ class PluginApi {
|
|||||||
*/
|
*/
|
||||||
addQuickSearchRandomTip(tip) {
|
addQuickSearchRandomTip(tip) {
|
||||||
addQuickSearchRandomTip(tip);
|
addQuickSearchRandomTip(tip);
|
||||||
addWidgetQuickSearchRandomTip(tip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1976,7 +1967,6 @@ class PluginApi {
|
|||||||
*/
|
*/
|
||||||
removeDefaultQuickSearchRandomTips() {
|
removeDefaultQuickSearchRandomTips() {
|
||||||
removeDefaultQuickSearchRandomTips();
|
removeDefaultQuickSearchRandomTips();
|
||||||
removeWidgetDefaultQuickSearchRandomTips();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,7 +249,7 @@ createWidget("header-icons", {
|
|||||||
icon: "search",
|
icon: "search",
|
||||||
iconId: SEARCH_BUTTON_ID,
|
iconId: SEARCH_BUTTON_ID,
|
||||||
action: "toggleSearchMenu",
|
action: "toggleSearchMenu",
|
||||||
active: attrs.searchVisible || this.search.visible,
|
active: this.search.visible,
|
||||||
href: getURL("/search"),
|
href: getURL("/search"),
|
||||||
classNames: ["search-dropdown"],
|
classNames: ["search-dropdown"],
|
||||||
});
|
});
|
||||||
@ -447,14 +447,14 @@ createWidget("revamped-user-menu-wrapper", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
createWidget("glimmer-search-menu-wrapper", {
|
createWidget("search-menu-wrapper", {
|
||||||
services: ["search"],
|
services: ["search"],
|
||||||
buildAttributes() {
|
buildAttributes() {
|
||||||
return { "data-click-outside": true, "aria-live": "polite" };
|
return { "aria-live": "polite" };
|
||||||
},
|
},
|
||||||
|
|
||||||
buildClasses() {
|
buildClasses() {
|
||||||
return ["search-menu glimmer-search-menu"];
|
return ["search-menu"];
|
||||||
},
|
},
|
||||||
|
|
||||||
html() {
|
html() {
|
||||||
@ -502,7 +502,7 @@ export default createWidget("header", {
|
|||||||
|
|
||||||
html(attrs, state) {
|
html(attrs, state) {
|
||||||
let inTopicRoute = false;
|
let inTopicRoute = false;
|
||||||
if (this.state.inTopicContext || this.search.inTopicContext) {
|
if (this.search.inTopicContext) {
|
||||||
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
|
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,7 +510,7 @@ export default createWidget("header", {
|
|||||||
const headerIcons = this.attach("header-icons", {
|
const headerIcons = this.attach("header-icons", {
|
||||||
hamburgerVisible: state.hamburgerVisible,
|
hamburgerVisible: state.hamburgerVisible,
|
||||||
userVisible: state.userVisible,
|
userVisible: state.userVisible,
|
||||||
searchVisible: state.searchVisible || this.search.visible,
|
searchVisible: this.search.visible,
|
||||||
flagCount: attrs.flagCount,
|
flagCount: attrs.flagCount,
|
||||||
user: this.currentUser,
|
user: this.currentUser,
|
||||||
sidebarEnabled: attrs.sidebarEnabled,
|
sidebarEnabled: attrs.sidebarEnabled,
|
||||||
@ -522,18 +522,9 @@ export default createWidget("header", {
|
|||||||
|
|
||||||
const panels = [this.attach("header-buttons", attrs), headerIcons];
|
const panels = [this.attach("header-buttons", attrs), headerIcons];
|
||||||
|
|
||||||
if (state.searchVisible || this.search.visible) {
|
if (this.search.visible) {
|
||||||
if (this.siteSettings.experimental_search_menu) {
|
this.search.inTopicContext = this.search.inTopicContext && inTopicRoute;
|
||||||
this.search.inTopicContext =
|
panels.push(this.attach("search-menu-wrapper"));
|
||||||
this.search.inTopicContext && inTopicRoute;
|
|
||||||
panels.push(this.attach("glimmer-search-menu-wrapper"));
|
|
||||||
} else {
|
|
||||||
panels.push(
|
|
||||||
this.attach("search-menu", {
|
|
||||||
inTopicContext: state.inTopicContext && inTopicRoute,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (state.hamburgerVisible) {
|
} else if (state.hamburgerVisible) {
|
||||||
panels.push(this.attach("hamburger-dropdown-wrapper", {}));
|
panels.push(this.attach("hamburger-dropdown-wrapper", {}));
|
||||||
} else if (state.userVisible) {
|
} else if (state.userVisible) {
|
||||||
@ -570,7 +561,7 @@ export default createWidget("header", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateHighlight() {
|
updateHighlight() {
|
||||||
if (!this.state.searchVisible || !this.search.visible) {
|
if (!this.search.visible) {
|
||||||
this.search.highlightTerm = "";
|
this.search.highlightTerm = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -578,7 +569,6 @@ export default createWidget("header", {
|
|||||||
closeAll() {
|
closeAll() {
|
||||||
this.state.userVisible = false;
|
this.state.userVisible = false;
|
||||||
this.state.hamburgerVisible = false;
|
this.state.hamburgerVisible = false;
|
||||||
this.state.searchVisible = false;
|
|
||||||
this.search.visible = false;
|
this.search.visible = false;
|
||||||
this.toggleBodyScrolling(false);
|
this.toggleBodyScrolling(false);
|
||||||
},
|
},
|
||||||
@ -619,15 +609,10 @@ export default createWidget("header", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.searchVisible = !this.state.searchVisible;
|
|
||||||
this.search.visible = !this.search.visible;
|
this.search.visible = !this.search.visible;
|
||||||
this.updateHighlight();
|
this.updateHighlight();
|
||||||
|
|
||||||
if (this.state.searchVisible) {
|
if (!this.search.searchVisible) {
|
||||||
// only used by the widget search-menu
|
|
||||||
this.focusSearchInput();
|
|
||||||
} else {
|
|
||||||
this.state.inTopicContext = false;
|
|
||||||
this.search.inTopicContext = false;
|
this.search.inTopicContext = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -664,10 +649,7 @@ export default createWidget("header", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
togglePageSearch() {
|
togglePageSearch() {
|
||||||
const { state } = this;
|
|
||||||
this.search.inTopicContext = false;
|
this.search.inTopicContext = false;
|
||||||
state.inTopicContext = false;
|
|
||||||
|
|
||||||
let showSearch = this.router.currentRouteName.startsWith("topic.");
|
let showSearch = this.router.currentRouteName.startsWith("topic.");
|
||||||
|
|
||||||
// If we're viewing a topic, only intercept search if there are cloaked posts
|
// If we're viewing a topic, only intercept search if there are cloaked posts
|
||||||
@ -681,13 +663,12 @@ export default createWidget("header", {
|
|||||||
$(".topic-post .cooked, .small-action:not(.time-gap)").length < total;
|
$(".topic-post .cooked, .small-action:not(.time-gap)").length < total;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.searchVisible || this.search.visible) {
|
if (this.search.visible) {
|
||||||
this.toggleSearchMenu();
|
this.toggleSearchMenu();
|
||||||
return showSearch;
|
return showSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSearch) {
|
if (showSearch) {
|
||||||
state.inTopicContext = true;
|
|
||||||
this.search.inTopicContext = true;
|
this.search.inTopicContext = true;
|
||||||
this.toggleSearchMenu();
|
this.toggleSearchMenu();
|
||||||
return false;
|
return false;
|
||||||
@ -699,12 +680,7 @@ export default createWidget("header", {
|
|||||||
domClean() {
|
domClean() {
|
||||||
const { state } = this;
|
const { state } = this;
|
||||||
|
|
||||||
if (
|
if (this.search.visible || state.hamburgerVisible || state.userVisible) {
|
||||||
state.searchVisible ||
|
|
||||||
this.search.visible ||
|
|
||||||
state.hamburgerVisible ||
|
|
||||||
state.userVisible
|
|
||||||
) {
|
|
||||||
this.closeAll();
|
this.closeAll();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -728,30 +704,4 @@ export default createWidget("header", {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// only used by the widget search-menu
|
|
||||||
focusSearchInput() {
|
|
||||||
if (
|
|
||||||
this.state.searchVisible &&
|
|
||||||
!this.siteSettings.experimental_search_menu
|
|
||||||
) {
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
const searchInput = document.querySelector("#search-term");
|
|
||||||
searchInput.focus();
|
|
||||||
searchInput.select();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// only used by the widget search-menu
|
|
||||||
setTopicContext() {
|
|
||||||
this.state.inTopicContext = true;
|
|
||||||
this.focusSearchInput();
|
|
||||||
},
|
|
||||||
|
|
||||||
// only used by the widget search-menu
|
|
||||||
clearContext() {
|
|
||||||
this.state.inTopicContext = false;
|
|
||||||
this.focusSearchInput();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { createWidget } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidget("search-term", {
|
|
||||||
tagName: "input",
|
|
||||||
buildId: () => "search-term",
|
|
||||||
buildKey: () => "search-term",
|
|
||||||
|
|
||||||
buildAttributes(attrs) {
|
|
||||||
return {
|
|
||||||
type: "text",
|
|
||||||
value: attrs.value || "",
|
|
||||||
autocomplete: "off",
|
|
||||||
placeholder: I18n.t("search.title"),
|
|
||||||
"aria-label": I18n.t("search.title"),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
input(e) {
|
|
||||||
const val = this.attrs.value;
|
|
||||||
|
|
||||||
// remove zero-width chars
|
|
||||||
const newVal = e.target.value.replace(/[\u200B-\u200D\uFEFF]/, "");
|
|
||||||
|
|
||||||
if (newVal !== val) {
|
|
||||||
this.sendWidgetAction("searchTermChanged", newVal);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: No longer used, remove in December 2021
|
|
||||||
createWidget("search-context", {
|
|
||||||
html() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,901 +0,0 @@
|
|||||||
import { hbs } from "ember-cli-htmlbars";
|
|
||||||
import { h } from "virtual-dom";
|
|
||||||
import { dateNode } from "discourse/helpers/node";
|
|
||||||
import highlightSearch from "discourse/lib/highlight-search";
|
|
||||||
import renderTag from "discourse/lib/render-tag";
|
|
||||||
import { emojiUnescape } from "discourse/lib/text";
|
|
||||||
import { escapeExpression, formatUsername } from "discourse/lib/utilities";
|
|
||||||
import User from "discourse/models/user";
|
|
||||||
import widgetHbs from "discourse/widgets/hbs-compiler";
|
|
||||||
import { avatarImg } from "discourse/widgets/post";
|
|
||||||
import RawHtml from "discourse/widgets/raw-html";
|
|
||||||
import RenderGlimmer from "discourse/widgets/render-glimmer";
|
|
||||||
import { MODIFIER_REGEXP } from "discourse/widgets/search-menu";
|
|
||||||
import { createWidget } from "discourse/widgets/widget";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import { deepMerge } from "discourse-common/lib/object";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
const suggestionShortcuts = [
|
|
||||||
"in:title",
|
|
||||||
"in:pinned",
|
|
||||||
"status:open",
|
|
||||||
"status:closed",
|
|
||||||
"status:public",
|
|
||||||
"status:noreplies",
|
|
||||||
"order:latest",
|
|
||||||
"order:views",
|
|
||||||
"order:likes",
|
|
||||||
"order:latest_topic",
|
|
||||||
];
|
|
||||||
|
|
||||||
const DEFAULT_QUICK_TIPS = [
|
|
||||||
{
|
|
||||||
label: "#",
|
|
||||||
description: I18n.t("search.tips.category_tag"),
|
|
||||||
clickable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "@",
|
|
||||||
description: I18n.t("search.tips.author"),
|
|
||||||
clickable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "in:",
|
|
||||||
description: I18n.t("search.tips.in"),
|
|
||||||
clickable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "status:",
|
|
||||||
description: I18n.t("search.tips.status"),
|
|
||||||
clickable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: I18n.t("search.tips.full_search_key", { modifier: "Ctrl" }),
|
|
||||||
description: I18n.t("search.tips.full_search"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "@me",
|
|
||||||
description: I18n.t("search.tips.me"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let QUICK_TIPS = [];
|
|
||||||
|
|
||||||
export function addSearchSuggestion(value) {
|
|
||||||
if (!suggestionShortcuts.includes(value)) {
|
|
||||||
suggestionShortcuts.push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addQuickSearchRandomTip(tip) {
|
|
||||||
if (!QUICK_TIPS.includes(tip)) {
|
|
||||||
QUICK_TIPS.push(tip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeDefaultQuickSearchRandomTips() {
|
|
||||||
QUICK_TIPS = QUICK_TIPS.filter((tip) => !DEFAULT_QUICK_TIPS.includes(tip));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetQuickSearchRandomTips() {
|
|
||||||
QUICK_TIPS = [].concat(DEFAULT_QUICK_TIPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetQuickSearchRandomTips();
|
|
||||||
|
|
||||||
class Highlighted extends RawHtml {
|
|
||||||
constructor(html, term) {
|
|
||||||
super({ html: `<span>${html}</span>` });
|
|
||||||
this.term = term;
|
|
||||||
}
|
|
||||||
|
|
||||||
decorate($html) {
|
|
||||||
highlightSearch($html[0], this.term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSearchResult({ type, linkField, builder }) {
|
|
||||||
return createWidget(`search-result-${type}`, {
|
|
||||||
tagName: "ul.list",
|
|
||||||
|
|
||||||
buildAttributes() {
|
|
||||||
return {
|
|
||||||
"aria-label": `${type} ${I18n.t("search.results")}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
return attrs.results.map((r) => {
|
|
||||||
let searchResultId;
|
|
||||||
|
|
||||||
if (type === "topic") {
|
|
||||||
searchResultId = r.topic_id;
|
|
||||||
} else {
|
|
||||||
searchResultId = r.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(
|
|
||||||
"li.item",
|
|
||||||
this.attach("link", {
|
|
||||||
href: r[linkField],
|
|
||||||
contents: () => builder.call(this, r, attrs.term),
|
|
||||||
className: "search-link",
|
|
||||||
searchResultId,
|
|
||||||
searchResultType: type,
|
|
||||||
searchLogId: attrs.searchLogId,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function postResult(result, link, term) {
|
|
||||||
const html = [link];
|
|
||||||
|
|
||||||
if (!this.site.mobileView) {
|
|
||||||
html.push(
|
|
||||||
h("span.blurb", [
|
|
||||||
dateNode(result.created_at),
|
|
||||||
h("span", " - "),
|
|
||||||
this.siteSettings.use_pg_headlines_for_excerpt
|
|
||||||
? new RawHtml({ html: `<span>${result.blurb}</span>` })
|
|
||||||
: new Highlighted(result.blurb, term),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "tag",
|
|
||||||
linkField: "url",
|
|
||||||
builder(t) {
|
|
||||||
const tag = escapeExpression(t.id);
|
|
||||||
return [
|
|
||||||
iconNode("tag"),
|
|
||||||
new RawHtml({ html: renderTag(tag, { tagName: "span" }) }),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "category",
|
|
||||||
linkField: "url",
|
|
||||||
builder(c) {
|
|
||||||
return this.attach("category-link", { category: c, link: false });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "group",
|
|
||||||
linkField: "url",
|
|
||||||
builder(group) {
|
|
||||||
const fullName = escapeExpression(group.fullName);
|
|
||||||
const name = escapeExpression(group.name);
|
|
||||||
const groupNames = [h("span.name", fullName || name)];
|
|
||||||
|
|
||||||
if (fullName) {
|
|
||||||
groupNames.push(h("span.slug", name));
|
|
||||||
}
|
|
||||||
|
|
||||||
let avatarFlair;
|
|
||||||
if (group.flairUrl) {
|
|
||||||
avatarFlair = this.attach("avatar-flair", {
|
|
||||||
flair_name: name,
|
|
||||||
flair_url: group.flairUrl,
|
|
||||||
flair_bg_color: group.flairBgColor,
|
|
||||||
flair_color: group.flairColor,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
avatarFlair = iconNode("users");
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupResultContents = [avatarFlair, h("div.group-names", groupNames)];
|
|
||||||
|
|
||||||
return h("div.group-result", groupResultContents);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "user",
|
|
||||||
linkField: "path",
|
|
||||||
builder(u) {
|
|
||||||
const userTitles = [];
|
|
||||||
|
|
||||||
if (u.name) {
|
|
||||||
userTitles.push(h("span.name", u.name));
|
|
||||||
}
|
|
||||||
|
|
||||||
userTitles.push(h("span.username", formatUsername(u.username)));
|
|
||||||
|
|
||||||
if (u.custom_data) {
|
|
||||||
u.custom_data.forEach((row) =>
|
|
||||||
userTitles.push(h("span.custom-field", `${row.name}: ${row.value}`))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userResultContents = [
|
|
||||||
avatarImg("small", {
|
|
||||||
template: u.avatar_template,
|
|
||||||
username: u.username,
|
|
||||||
}),
|
|
||||||
h("div.user-titles", userTitles),
|
|
||||||
];
|
|
||||||
|
|
||||||
return h("div.user-result", userResultContents);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "topic",
|
|
||||||
linkField: "url",
|
|
||||||
builder(result, term) {
|
|
||||||
const topic = result.topic;
|
|
||||||
|
|
||||||
const firstLine = [
|
|
||||||
this.attach("topic-status", { topic, disableActions: true }),
|
|
||||||
h(
|
|
||||||
"span.topic-title",
|
|
||||||
{ attributes: { "data-topic-id": topic.id } },
|
|
||||||
this.siteSettings.use_pg_headlines_for_excerpt &&
|
|
||||||
result.topic_title_headline
|
|
||||||
? new RawHtml({
|
|
||||||
html: `<span>${emojiUnescape(
|
|
||||||
result.topic_title_headline
|
|
||||||
)}</span>`,
|
|
||||||
})
|
|
||||||
: new Highlighted(topic.fancyTitle, term)
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const secondLine = [
|
|
||||||
this.attach("category-link", {
|
|
||||||
category: topic.category,
|
|
||||||
link: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
if (this.siteSettings.tagging_enabled) {
|
|
||||||
secondLine.push(
|
|
||||||
this.attach("discourse-tags", { topic, tagName: "span" })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const link = h("span.topic", [
|
|
||||||
h("span.first-line", firstLine),
|
|
||||||
h("span.second-line", secondLine),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return postResult.call(this, result, link, term);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createSearchResult({
|
|
||||||
type: "post",
|
|
||||||
linkField: "url",
|
|
||||||
builder(result, term) {
|
|
||||||
return postResult.call(
|
|
||||||
this,
|
|
||||||
result,
|
|
||||||
I18n.t("search.post_format", result),
|
|
||||||
term
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("search-menu-results", {
|
|
||||||
tagName: "div.results",
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const {
|
|
||||||
term,
|
|
||||||
suggestionKeyword,
|
|
||||||
inTopicContext,
|
|
||||||
results,
|
|
||||||
searchTopics,
|
|
||||||
onLinkClicked,
|
|
||||||
} = attrs;
|
|
||||||
|
|
||||||
if (suggestionKeyword) {
|
|
||||||
return this.attach("search-menu-assistant", {
|
|
||||||
term,
|
|
||||||
suggestionKeyword,
|
|
||||||
results: attrs.suggestionResults || [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchTopics && attrs.invalidTerm) {
|
|
||||||
return h("div.no-results", I18n.t("search.too_short"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchTopics && attrs.noResults) {
|
|
||||||
return h("div.no-results", I18n.t("search.no_results"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!term && !attrs.inPMInboxContext) {
|
|
||||||
return this.attach("search-menu-initial-options", { term });
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultTypes = results.resultTypes || [];
|
|
||||||
|
|
||||||
const mainResultsContent = [];
|
|
||||||
const usersAndGroups = [];
|
|
||||||
const categoriesAndTags = [];
|
|
||||||
|
|
||||||
const buildMoreNode = (result) => {
|
|
||||||
const moreArgs = {
|
|
||||||
className: "filter search-link",
|
|
||||||
contents: () => [I18n.t("more"), "..."],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result.moreUrl) {
|
|
||||||
return this.attach(
|
|
||||||
"link",
|
|
||||||
deepMerge(moreArgs, {
|
|
||||||
href: result.moreUrl,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (result.more) {
|
|
||||||
return this.attach(
|
|
||||||
"link",
|
|
||||||
deepMerge(moreArgs, {
|
|
||||||
action: "moreOfType",
|
|
||||||
actionParam: result.type,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const assignContainer = (result, node) => {
|
|
||||||
if (searchTopics) {
|
|
||||||
if (["topic"].includes(result.type)) {
|
|
||||||
mainResultsContent.push(node);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (["user", "group"].includes(result.type)) {
|
|
||||||
usersAndGroups.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (["category", "tag"].includes(result.type)) {
|
|
||||||
categoriesAndTags.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
resultTypes.forEach((rt) => {
|
|
||||||
const resultNodeContents = [
|
|
||||||
this.attach(rt.componentName, {
|
|
||||||
searchLogId: attrs.results.grouped_search_result.search_log_id,
|
|
||||||
results: rt.results,
|
|
||||||
term,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (["topic"].includes(rt.type)) {
|
|
||||||
const more = buildMoreNode(rt);
|
|
||||||
if (more) {
|
|
||||||
resultNodeContents.push(h("div.search-menu__show-more", more));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assignContainer(rt, h(`div.${rt.componentName}`, resultNodeContents));
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = [];
|
|
||||||
|
|
||||||
if (!searchTopics) {
|
|
||||||
if (!attrs.inPMInboxContext) {
|
|
||||||
content.push(this.attach("search-menu-initial-options", { term }));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mainResultsContent.length) {
|
|
||||||
content.push(mainResultsContent);
|
|
||||||
} else {
|
|
||||||
return h("div.no-results", I18n.t("search.no_results"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
new RenderGlimmer(
|
|
||||||
this,
|
|
||||||
"div",
|
|
||||||
hbs`<PluginOutlet @name="search-menu-results-top" @outletArgs={{@data.outletArgs}}/>`,
|
|
||||||
{
|
|
||||||
outletArgs: {
|
|
||||||
searchTerm: term,
|
|
||||||
inTopicContext,
|
|
||||||
onLinkClicked,
|
|
||||||
searchTopics,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
content.push(categoriesAndTags);
|
|
||||||
content.push(usersAndGroups);
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
new RenderGlimmer(
|
|
||||||
this,
|
|
||||||
"div",
|
|
||||||
hbs`<PluginOutlet @name="search-menu-results-bottom" @outletArgs={{@data.outletArgs}}/>`,
|
|
||||||
{
|
|
||||||
outletArgs: {
|
|
||||||
searchTerm: term,
|
|
||||||
inTopicContext,
|
|
||||||
onLinkClicked,
|
|
||||||
searchTopics,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return content;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("search-menu-assistant", {
|
|
||||||
tagName: "ul.search-menu-assistant",
|
|
||||||
buildKey: () => `search-menu-assistant`,
|
|
||||||
services: ["router"],
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
if (this.currentUser) {
|
|
||||||
addSearchSuggestion("in:likes");
|
|
||||||
addSearchSuggestion("in:bookmarks");
|
|
||||||
addSearchSuggestion("in:mine");
|
|
||||||
addSearchSuggestion("in:messages");
|
|
||||||
addSearchSuggestion("in:seen");
|
|
||||||
addSearchSuggestion("in:tracking");
|
|
||||||
addSearchSuggestion("in:unseen");
|
|
||||||
addSearchSuggestion("in:watching");
|
|
||||||
}
|
|
||||||
if (this.siteSettings.tagging_enabled) {
|
|
||||||
addSearchSuggestion("in:tagged");
|
|
||||||
addSearchSuggestion("in:untagged");
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = [];
|
|
||||||
const { suggestionKeyword, term } = attrs;
|
|
||||||
|
|
||||||
let prefix;
|
|
||||||
if (suggestionKeyword !== "+") {
|
|
||||||
prefix = term?.split(suggestionKeyword)[0].trim() || "";
|
|
||||||
|
|
||||||
if (prefix.length) {
|
|
||||||
prefix = `${prefix} `;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (suggestionKeyword) {
|
|
||||||
case "+":
|
|
||||||
attrs.results.forEach((item) => {
|
|
||||||
if (item.additionalTags) {
|
|
||||||
prefix = term?.split(" ").slice(0, -1).join(" ").trim() || "";
|
|
||||||
} else {
|
|
||||||
prefix = term?.split("#")[0].trim() || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefix.length) {
|
|
||||||
prefix = `${prefix} `;
|
|
||||||
}
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
prefix,
|
|
||||||
tag: item.tagName,
|
|
||||||
additionalTags: item.additionalTags,
|
|
||||||
category: item.category,
|
|
||||||
slug: term,
|
|
||||||
withInLabel: attrs.withInLabel,
|
|
||||||
isIntersection: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "#":
|
|
||||||
attrs.results.forEach((item) => {
|
|
||||||
if (item.model) {
|
|
||||||
const fullSlug = item.model.parentCategory
|
|
||||||
? `#${item.model.parentCategory.slug}:${item.model.slug}`
|
|
||||||
: `#${item.model.slug}`;
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
prefix,
|
|
||||||
category: item.model,
|
|
||||||
slug: `${prefix}${fullSlug}`,
|
|
||||||
withInLabel: attrs.withInLabel,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
prefix,
|
|
||||||
tag: item.name,
|
|
||||||
slug: `${prefix}#${item.name}`,
|
|
||||||
withInLabel: attrs.withInLabel,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "@":
|
|
||||||
// when only one user matches while in topic
|
|
||||||
// quick suggest user search in the topic or globally
|
|
||||||
if (
|
|
||||||
attrs.results.length === 1 &&
|
|
||||||
this.router.currentRouteName.startsWith("topic.")
|
|
||||||
) {
|
|
||||||
const user = attrs.results[0];
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
extraHint: I18n.t("search.enter_hint"),
|
|
||||||
prefix,
|
|
||||||
user,
|
|
||||||
slug: `${prefix}@${user.username}`,
|
|
||||||
suffix: h(
|
|
||||||
"span.label-suffix",
|
|
||||||
` ${I18n.t("search.in_topics_posts")}`
|
|
||||||
),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
prefix,
|
|
||||||
user,
|
|
||||||
setTopicContext: true,
|
|
||||||
slug: `${prefix}@${user.username}`,
|
|
||||||
suffix: h(
|
|
||||||
"span.label-suffix",
|
|
||||||
` ${I18n.t("search.in_this_topic")}`
|
|
||||||
),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
attrs.results.forEach((user) => {
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
prefix,
|
|
||||||
user,
|
|
||||||
slug: `${prefix}@${user.username}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
suggestionShortcuts.forEach((item) => {
|
|
||||||
if (item.includes(suggestionKeyword) || !suggestionKeyword) {
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
slug: `${prefix}${item}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return content.filter((c, i) => i <= 8);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("search-menu-initial-options", {
|
|
||||||
tagName: "ul.search-menu-initial-options",
|
|
||||||
services: ["search"],
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
if (attrs.term?.match(MODIFIER_REGEXP)) {
|
|
||||||
return this.defaultRow(attrs.term);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctx = this.search.searchContext;
|
|
||||||
const content = [];
|
|
||||||
|
|
||||||
if (attrs.term || ctx) {
|
|
||||||
if (attrs.term) {
|
|
||||||
content.push(this.defaultRow(attrs.term, { withLabel: true }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx) {
|
|
||||||
const term = attrs.term || "";
|
|
||||||
switch (ctx.type) {
|
|
||||||
case "topic":
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
slug: term,
|
|
||||||
setTopicContext: true,
|
|
||||||
label: [
|
|
||||||
h("span", `${term} `),
|
|
||||||
h("span.label-suffix", I18n.t("search.in_this_topic")),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "private_messages":
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
slug: `${term} in:messages`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "category":
|
|
||||||
const fullSlug = ctx.category.parentCategory
|
|
||||||
? `#${ctx.category.parentCategory.slug}:${ctx.category.slug}`
|
|
||||||
: `#${ctx.category.slug}`;
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant", {
|
|
||||||
term: `${term} ${fullSlug}`,
|
|
||||||
suggestionKeyword: "#",
|
|
||||||
results: [{ model: ctx.category }],
|
|
||||||
withInLabel: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "tag":
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant", {
|
|
||||||
term: `${term} #${ctx.name}`,
|
|
||||||
suggestionKeyword: "#",
|
|
||||||
results: [{ name: ctx.name }],
|
|
||||||
withInLabel: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "tagIntersection":
|
|
||||||
let tagTerm;
|
|
||||||
if (ctx.additionalTags) {
|
|
||||||
const tags = [ctx.tagId, ...ctx.additionalTags];
|
|
||||||
tagTerm = `${term} tags:${tags.join("+")}`;
|
|
||||||
} else {
|
|
||||||
tagTerm = `${term} #${ctx.tagId}`;
|
|
||||||
}
|
|
||||||
let suggestionOptions = {
|
|
||||||
tagName: ctx.tagId,
|
|
||||||
additionalTags: ctx.additionalTags,
|
|
||||||
};
|
|
||||||
if (ctx.category) {
|
|
||||||
const categorySlug = ctx.category.parentCategory
|
|
||||||
? `#${ctx.category.parentCategory.slug}:${ctx.category.slug}`
|
|
||||||
: `#${ctx.category.slug}`;
|
|
||||||
suggestionOptions.categoryName = categorySlug;
|
|
||||||
suggestionOptions.category = ctx.category;
|
|
||||||
tagTerm = tagTerm + ` ${categorySlug}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant", {
|
|
||||||
term: tagTerm,
|
|
||||||
suggestionKeyword: "+",
|
|
||||||
results: [suggestionOptions],
|
|
||||||
withInLabel: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "user":
|
|
||||||
content.push(
|
|
||||||
this.attach("search-menu-assistant-item", {
|
|
||||||
slug: `${term} @${ctx.user.username}`,
|
|
||||||
label: [
|
|
||||||
h("span", `${term} `),
|
|
||||||
h(
|
|
||||||
"span.label-suffix",
|
|
||||||
I18n.t("search.in_posts_by", {
|
|
||||||
username: ctx.user.username,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.length === 0) {
|
|
||||||
content.push(this.attach("random-quick-tip"));
|
|
||||||
|
|
||||||
if (this.currentUser && this.siteSettings.log_search_queries) {
|
|
||||||
if (this.currentUser.recent_searches?.length) {
|
|
||||||
content.push(this.attach("search-menu-recent-searches"));
|
|
||||||
} else {
|
|
||||||
this.loadRecentSearches();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
},
|
|
||||||
|
|
||||||
defaultRow(term, opts = { withLabel: false }) {
|
|
||||||
return this.attach("search-menu-assistant-item", {
|
|
||||||
slug: term,
|
|
||||||
extraHint: I18n.t("search.enter_hint"),
|
|
||||||
label: [
|
|
||||||
h("span.keyword", `${term}`),
|
|
||||||
opts.withLabel
|
|
||||||
? h("span.label-suffix", I18n.t("search.in_topics_posts"))
|
|
||||||
: null,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshSearchMenuResults() {
|
|
||||||
this.scheduleRerender();
|
|
||||||
},
|
|
||||||
|
|
||||||
loadRecentSearches() {
|
|
||||||
User.loadRecentSearches().then((result) => {
|
|
||||||
if (result.success && result.recent_searches?.length) {
|
|
||||||
this.currentUser.set(
|
|
||||||
"recent_searches",
|
|
||||||
Object.assign(result.recent_searches)
|
|
||||||
);
|
|
||||||
this.scheduleRerender();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("search-menu-assistant-item", {
|
|
||||||
tagName: "li.search-menu-assistant-item",
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const prefix = attrs.prefix?.trim();
|
|
||||||
const attributes = {};
|
|
||||||
attributes.href = "#";
|
|
||||||
|
|
||||||
let content = [
|
|
||||||
h(
|
|
||||||
"span",
|
|
||||||
{ attributes: { "aria-label": I18n.t("search.title") } },
|
|
||||||
iconNode(attrs.icon || "search")
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (prefix) {
|
|
||||||
content.push(h("span.search-item-prefix", `${prefix} `));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attrs.withInLabel) {
|
|
||||||
content.push(h("span.label-suffix", `${I18n.t("search.in")} `));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attrs.category) {
|
|
||||||
attributes.href = attrs.category.url;
|
|
||||||
|
|
||||||
content.push(
|
|
||||||
this.attach("category-link", {
|
|
||||||
category: attrs.category,
|
|
||||||
allowUncategorized: true,
|
|
||||||
recursive: true,
|
|
||||||
link: false,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// category and tag combination
|
|
||||||
if (attrs.tag && attrs.isIntersection) {
|
|
||||||
attributes.href = getURL(`/tag/${attrs.tag}`);
|
|
||||||
content.push(h("span.search-item-tag", [iconNode("tag"), attrs.tag]));
|
|
||||||
}
|
|
||||||
} else if (attrs.tag) {
|
|
||||||
if (attrs.isIntersection && attrs.additionalTags?.length) {
|
|
||||||
const tags = [attrs.tag, ...attrs.additionalTags];
|
|
||||||
content.push(h("span.search-item-tag", `tags:${tags.join("+")}`));
|
|
||||||
} else {
|
|
||||||
attributes.href = getURL(`/tag/${attrs.tag}`);
|
|
||||||
content.push(h("span.search-item-tag", [iconNode("tag"), attrs.tag]));
|
|
||||||
}
|
|
||||||
} else if (attrs.user) {
|
|
||||||
const userResult = [
|
|
||||||
avatarImg("small", {
|
|
||||||
template: attrs.user.avatar_template,
|
|
||||||
username: attrs.user.username,
|
|
||||||
}),
|
|
||||||
h("span.username", formatUsername(attrs.user.username)),
|
|
||||||
attrs.suffix,
|
|
||||||
];
|
|
||||||
content.push(h("span.search-item-user", userResult));
|
|
||||||
} else {
|
|
||||||
content.push(h("span.search-item-slug", attrs.label || attrs.slug));
|
|
||||||
}
|
|
||||||
if (attrs.extraHint) {
|
|
||||||
content.push(h("span.extra-hint", attrs.extraHint));
|
|
||||||
}
|
|
||||||
return h("a.widget-link.search-link", { attributes }, content);
|
|
||||||
},
|
|
||||||
|
|
||||||
click(e) {
|
|
||||||
const searchInput = document.querySelector("#search-term");
|
|
||||||
searchInput.value = this.attrs.slug;
|
|
||||||
searchInput.focus();
|
|
||||||
this.sendWidgetAction("triggerAutocomplete", {
|
|
||||||
value: this.attrs.slug,
|
|
||||||
searchTopics: true,
|
|
||||||
setTopicContext: this.attrs.setTopicContext,
|
|
||||||
origin: this.attrs.origin,
|
|
||||||
});
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("random-quick-tip", {
|
|
||||||
tagName: "li.search-random-quick-tip",
|
|
||||||
|
|
||||||
buildKey: () => "random-quick-tip",
|
|
||||||
|
|
||||||
defaultState() {
|
|
||||||
return QUICK_TIPS.length
|
|
||||||
? QUICK_TIPS[Math.floor(Math.random() * QUICK_TIPS.length)]
|
|
||||||
: {};
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs, state) {
|
|
||||||
if (!Object.keys(state).length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
h(
|
|
||||||
`span.tip-label${state.clickable ? ".tip-clickable" : ""}`,
|
|
||||||
state.label
|
|
||||||
),
|
|
||||||
h("span.tip-description", state.description),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
click(e) {
|
|
||||||
if (e.target.classList.contains("tip-clickable")) {
|
|
||||||
const searchInput = document.querySelector("#search-term");
|
|
||||||
searchInput.value = this.state.label;
|
|
||||||
searchInput.focus();
|
|
||||||
this.sendWidgetAction("triggerAutocomplete", {
|
|
||||||
value: this.state.label,
|
|
||||||
searchTopics: this.state.searchTopics,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("search-menu-recent-searches", {
|
|
||||||
tagName: "div.search-menu-recent",
|
|
||||||
|
|
||||||
template: widgetHbs`
|
|
||||||
<div class="heading">
|
|
||||||
<h4>{{i18n "search.recent"}}</h4>
|
|
||||||
{{flat-button
|
|
||||||
className="clear-recent-searches"
|
|
||||||
title="search.clear_recent"
|
|
||||||
icon="times"
|
|
||||||
action="clearRecent"
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#each this.currentUser.recent_searches as |slug|}}
|
|
||||||
{{attach
|
|
||||||
widget="search-menu-assistant-item"
|
|
||||||
attrs=(hash slug=slug icon="history" origin="recent-search")
|
|
||||||
}}
|
|
||||||
{{/each}}
|
|
||||||
`,
|
|
||||||
|
|
||||||
clearRecent() {
|
|
||||||
return User.resetRecentSearches().then((result) => {
|
|
||||||
if (result.success) {
|
|
||||||
this.currentUser.recent_searches.clear();
|
|
||||||
this.sendWidgetAction("refreshSearchMenuResults");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,595 +0,0 @@
|
|||||||
import { cancel } from "@ember/runloop";
|
|
||||||
import { Promise } from "rsvp";
|
|
||||||
import { h } from "virtual-dom";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
import { CANCELLED_STATUS } from "discourse/lib/autocomplete";
|
|
||||||
import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
|
|
||||||
import {
|
|
||||||
isValidSearchTerm,
|
|
||||||
searchForTerm,
|
|
||||||
updateRecentSearches,
|
|
||||||
} from "discourse/lib/search";
|
|
||||||
import DiscourseURL from "discourse/lib/url";
|
|
||||||
import userSearch from "discourse/lib/user-search";
|
|
||||||
import { isiPad, translateModKey } from "discourse/lib/utilities";
|
|
||||||
import { createWidget } from "discourse/widgets/widget";
|
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
const CATEGORY_SLUG_REGEXP = /(\#[a-zA-Z0-9\-:]*)$/gi;
|
|
||||||
const USERNAME_REGEXP = /(\@[a-zA-Z0-9\-\_]*)$/gi;
|
|
||||||
const SUGGESTIONS_REGEXP = /(in:|status:|order:|:)([a-zA-Z]*)$/gi;
|
|
||||||
const SECOND_ENTER_MAX_DELAY = 15000;
|
|
||||||
export const MODIFIER_REGEXP = /.*(\#|\@|:).*$/gi;
|
|
||||||
export const DEFAULT_TYPE_FILTER = "exclude_topics";
|
|
||||||
|
|
||||||
const searchData = {};
|
|
||||||
|
|
||||||
const onKeyDownCallbacks = [];
|
|
||||||
|
|
||||||
export function addOnKeyDownCallback(fn) {
|
|
||||||
onKeyDownCallbacks.push(fn);
|
|
||||||
}
|
|
||||||
export function resetOnKeyDownCallbacks() {
|
|
||||||
onKeyDownCallbacks.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initSearchData() {
|
|
||||||
searchData.loading = false;
|
|
||||||
searchData.results = {};
|
|
||||||
searchData.noResults = false;
|
|
||||||
searchData.term = undefined;
|
|
||||||
searchData.typeFilter = DEFAULT_TYPE_FILTER;
|
|
||||||
searchData.invalidTerm = false;
|
|
||||||
searchData.suggestionResults = [];
|
|
||||||
searchData.suggestionKeyword = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initSearchData();
|
|
||||||
|
|
||||||
// Helps with debouncing and cancelling promises
|
|
||||||
const SearchHelper = {
|
|
||||||
_activeSearch: null,
|
|
||||||
|
|
||||||
// for cancelling debounced search
|
|
||||||
cancel() {
|
|
||||||
if (this._activeSearch) {
|
|
||||||
this._activeSearch.abort();
|
|
||||||
this._activeSearch = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
perform(widget) {
|
|
||||||
this.cancel();
|
|
||||||
|
|
||||||
const { term, typeFilter } = searchData;
|
|
||||||
const searchContext = widget.searchContext();
|
|
||||||
|
|
||||||
const fullSearchUrl = widget.fullSearchUrl();
|
|
||||||
const matchSuggestions = this.matchesSuggestions();
|
|
||||||
|
|
||||||
if (matchSuggestions) {
|
|
||||||
searchData.noResults = true;
|
|
||||||
searchData.results = {};
|
|
||||||
searchData.loading = false;
|
|
||||||
searchData.suggestionResults = [];
|
|
||||||
|
|
||||||
if (matchSuggestions.type === "category") {
|
|
||||||
const categorySearchTerm = matchSuggestions.categoriesMatch[0].replace(
|
|
||||||
"#",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
const categoryTagSearch = searchCategoryTag(
|
|
||||||
categorySearchTerm,
|
|
||||||
widget.siteSettings
|
|
||||||
);
|
|
||||||
Promise.resolve(categoryTagSearch).then((results) => {
|
|
||||||
if (results !== CANCELLED_STATUS) {
|
|
||||||
searchData.suggestionResults = results;
|
|
||||||
searchData.suggestionKeyword = "#";
|
|
||||||
}
|
|
||||||
widget.scheduleRerender();
|
|
||||||
});
|
|
||||||
} else if (matchSuggestions.type === "username") {
|
|
||||||
const userSearchTerm = matchSuggestions.usernamesMatch[0].replace(
|
|
||||||
"@",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
const opts = { includeGroups: true, limit: 6 };
|
|
||||||
if (userSearchTerm.length > 0) {
|
|
||||||
opts.term = userSearchTerm;
|
|
||||||
} else {
|
|
||||||
opts.lastSeenUsers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
userSearch(opts).then((result) => {
|
|
||||||
if (result?.users?.length > 0) {
|
|
||||||
searchData.suggestionResults = result.users;
|
|
||||||
searchData.suggestionKeyword = "@";
|
|
||||||
} else {
|
|
||||||
searchData.noResults = true;
|
|
||||||
searchData.suggestionKeyword = false;
|
|
||||||
}
|
|
||||||
widget.scheduleRerender();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
searchData.suggestionKeyword = matchSuggestions[0];
|
|
||||||
widget.scheduleRerender();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
searchData.suggestionKeyword = false;
|
|
||||||
|
|
||||||
if (!term) {
|
|
||||||
searchData.noResults = false;
|
|
||||||
searchData.results = {};
|
|
||||||
searchData.loading = false;
|
|
||||||
searchData.invalidTerm = false;
|
|
||||||
|
|
||||||
widget.scheduleRerender();
|
|
||||||
} else if (!isValidSearchTerm(term, widget.siteSettings)) {
|
|
||||||
searchData.noResults = true;
|
|
||||||
searchData.results = {};
|
|
||||||
searchData.loading = false;
|
|
||||||
searchData.invalidTerm = true;
|
|
||||||
|
|
||||||
widget.scheduleRerender();
|
|
||||||
} else {
|
|
||||||
searchData.invalidTerm = false;
|
|
||||||
|
|
||||||
this._activeSearch = searchForTerm(term, {
|
|
||||||
typeFilter,
|
|
||||||
fullSearchUrl,
|
|
||||||
searchContext,
|
|
||||||
});
|
|
||||||
this._activeSearch
|
|
||||||
.then((results) => {
|
|
||||||
// we ensure the current search term is the one used
|
|
||||||
// when starting the query
|
|
||||||
if (results && term === searchData.term) {
|
|
||||||
if (searchContext) {
|
|
||||||
widget.appEvents.trigger("post-stream:refresh", { force: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
searchData.noResults = results.resultTypes.length === 0;
|
|
||||||
searchData.results = results;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(popupAjaxError)
|
|
||||||
.finally(() => {
|
|
||||||
searchData.loading = false;
|
|
||||||
widget.scheduleRerender();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
matchesSuggestions() {
|
|
||||||
if (searchData.term === undefined || this.includesTopics()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const term = searchData.term.trim();
|
|
||||||
const categoriesMatch = term.match(CATEGORY_SLUG_REGEXP);
|
|
||||||
|
|
||||||
if (categoriesMatch) {
|
|
||||||
return { type: "category", categoriesMatch };
|
|
||||||
}
|
|
||||||
|
|
||||||
const usernamesMatch = term.match(USERNAME_REGEXP);
|
|
||||||
if (usernamesMatch) {
|
|
||||||
return { type: "username", usernamesMatch };
|
|
||||||
}
|
|
||||||
|
|
||||||
const suggestionsMatch = term.match(SUGGESTIONS_REGEXP);
|
|
||||||
if (suggestionsMatch) {
|
|
||||||
return suggestionsMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
includesTopics() {
|
|
||||||
return searchData.typeFilter !== DEFAULT_TYPE_FILTER;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createWidget("search-menu", {
|
|
||||||
tagName: "div.search-menu",
|
|
||||||
services: ["search"],
|
|
||||||
searchData,
|
|
||||||
|
|
||||||
buildAttributes() {
|
|
||||||
return {
|
|
||||||
"aria-live": "polite",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
buildKey: () => "search-menu",
|
|
||||||
|
|
||||||
defaultState(attrs) {
|
|
||||||
return {
|
|
||||||
inTopicContext: attrs.inTopicContext,
|
|
||||||
inPMInboxContext: this.search?.searchContext?.type === "private_messages",
|
|
||||||
_lastEnterTimestamp: null,
|
|
||||||
_debouncer: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
fullSearchUrl(opts) {
|
|
||||||
let url = "/search";
|
|
||||||
const params = [];
|
|
||||||
|
|
||||||
if (searchData.term) {
|
|
||||||
let query = "";
|
|
||||||
|
|
||||||
query += `q=${encodeURIComponent(searchData.term)}`;
|
|
||||||
|
|
||||||
const searchContext = this.searchContext();
|
|
||||||
|
|
||||||
if (searchContext?.type === "topic") {
|
|
||||||
query += encodeURIComponent(` topic:${searchContext.id}`);
|
|
||||||
} else if (searchContext?.type === "private_messages") {
|
|
||||||
query += encodeURIComponent(` in:messages`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
params.push(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts && opts.expanded) {
|
|
||||||
params.push("expanded=true");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.length > 0) {
|
|
||||||
url = `${url}?${params.join("&")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getURL(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
panelContents() {
|
|
||||||
let searchInput = [];
|
|
||||||
if (this.state.inTopicContext) {
|
|
||||||
searchInput.push(
|
|
||||||
this.attach("button", {
|
|
||||||
icon: "times",
|
|
||||||
label: "search.in_this_topic",
|
|
||||||
title: "search.in_this_topic_tooltip",
|
|
||||||
className: "btn btn-small search-context",
|
|
||||||
action: "clearTopicContext",
|
|
||||||
iconRight: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (this.state.inPMInboxContext) {
|
|
||||||
searchInput.push(
|
|
||||||
this.attach("button", {
|
|
||||||
icon: "times",
|
|
||||||
label: "search.in_messages",
|
|
||||||
title: "search.in_messages_tooltip",
|
|
||||||
className: "btn btn-small search-context",
|
|
||||||
action: "clearPMInboxContext",
|
|
||||||
iconRight: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
searchInput.push(this.attach("search-term", { value: searchData.term }));
|
|
||||||
|
|
||||||
if (searchData.loading) {
|
|
||||||
searchInput.push(h("div.searching", h("div.spinner")));
|
|
||||||
} else {
|
|
||||||
const clearButton = this.attach("link", {
|
|
||||||
title: "search.clear_search",
|
|
||||||
action: "clearSearch",
|
|
||||||
className: "clear-search",
|
|
||||||
contents: () => iconNode("times"),
|
|
||||||
});
|
|
||||||
|
|
||||||
const advancedSearchButton = this.attach("link", {
|
|
||||||
href: this.fullSearchUrl({ expanded: true }),
|
|
||||||
contents: () => iconNode("sliders-h"),
|
|
||||||
className: "show-advanced-search",
|
|
||||||
title: "search.open_advanced",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (searchData.term) {
|
|
||||||
searchInput.push(
|
|
||||||
h("div.searching", [clearButton, advancedSearchButton])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
searchInput.push(h("div.searching", advancedSearchButton));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = [h("div.search-input", searchInput)];
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.state.inTopicContext &&
|
|
||||||
(!SearchHelper.includesTopics() || !searchData.term)
|
|
||||||
) {
|
|
||||||
const isMobileDevice = this.site.isMobileDevice;
|
|
||||||
|
|
||||||
if (!isMobileDevice) {
|
|
||||||
results.push(this.attach("browser-search-tip"));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!searchData.loading) {
|
|
||||||
results.push(
|
|
||||||
this.attach("search-menu-results", {
|
|
||||||
term: searchData.term,
|
|
||||||
noResults: searchData.noResults,
|
|
||||||
results: searchData.results,
|
|
||||||
invalidTerm: searchData.invalidTerm,
|
|
||||||
suggestionKeyword: searchData.suggestionKeyword,
|
|
||||||
suggestionResults: searchData.suggestionResults,
|
|
||||||
searchTopics: SearchHelper.includesTopics(),
|
|
||||||
inPMInboxContext: this.state.inPMInboxContext,
|
|
||||||
inTopicContext: this.state.inTopicContext,
|
|
||||||
onLinkClicked: this.onLinkClicked.bind(this),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSearch() {
|
|
||||||
searchData.term = "";
|
|
||||||
const searchInput = document.getElementById("search-term");
|
|
||||||
searchInput.value = "";
|
|
||||||
searchInput.focus();
|
|
||||||
this.triggerSearch();
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs, state) {
|
|
||||||
if (attrs.inTopicContext === false) {
|
|
||||||
state.inTopicContext = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.attach("menu-panel", {
|
|
||||||
maxWidth: 500,
|
|
||||||
contents: () => this.panelContents(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onLinkClicked() {
|
|
||||||
return this.sendWidgetAction("linkClickedEvent");
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseDown(e) {
|
|
||||||
if (e.target === document.querySelector("input#search-term")) {
|
|
||||||
this.state.inputSelectionEvent = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clickOutside() {
|
|
||||||
if (this.key === "search-menu" && !this.state.inputSelectionEvent) {
|
|
||||||
this.sendWidgetAction("toggleSearchMenu");
|
|
||||||
}
|
|
||||||
this.state.inputSelectionEvent = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
clearTopicContext() {
|
|
||||||
this.sendWidgetAction("clearContext");
|
|
||||||
},
|
|
||||||
|
|
||||||
clearPMInboxContext() {
|
|
||||||
this.state.inPMInboxContext = false;
|
|
||||||
this.sendWidgetAction("focusSearchInput");
|
|
||||||
},
|
|
||||||
|
|
||||||
keyDown(e) {
|
|
||||||
if (
|
|
||||||
onKeyDownCallbacks.length &&
|
|
||||||
!onKeyDownCallbacks.some((fn) => fn(this, e))
|
|
||||||
) {
|
|
||||||
// Return early if any callbacks return false
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleKeyDown(e);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleKeyDown(e) {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
this.sendWidgetAction("toggleSearchMenu");
|
|
||||||
document.querySelector("#search-button").focus();
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchData.loading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.which === 65 /* a */) {
|
|
||||||
if (document.activeElement?.classList.contains("search-link")) {
|
|
||||||
if (document.querySelector("#reply-control.open")) {
|
|
||||||
// add a link and focus composer
|
|
||||||
|
|
||||||
this.appEvents.trigger(
|
|
||||||
"composer:insert-text",
|
|
||||||
document.activeElement.href,
|
|
||||||
{
|
|
||||||
ensureSpace: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.appEvents.trigger("header:keyboard-trigger", { type: "search" });
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
document.querySelector("#reply-control.open textarea").focus();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const up = e.key === "ArrowUp";
|
|
||||||
const down = e.key === "ArrowDown";
|
|
||||||
if (up || down) {
|
|
||||||
let focused = document.activeElement.closest(".search-menu")
|
|
||||||
? document.activeElement
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (!focused) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let links = document.querySelectorAll(".search-menu .results a");
|
|
||||||
let results = document.querySelectorAll(
|
|
||||||
".search-menu .results .search-link"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!results.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prevResult;
|
|
||||||
let result;
|
|
||||||
|
|
||||||
links.forEach((item) => {
|
|
||||||
if (item.classList.contains("search-link")) {
|
|
||||||
prevResult = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item === focused) {
|
|
||||||
result = prevResult;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let index = -1;
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
index = Array.prototype.indexOf.call(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index === -1 && down) {
|
|
||||||
document.querySelector(".search-menu .results .search-link").focus();
|
|
||||||
} else if (index === 0 && up) {
|
|
||||||
document.querySelector(".search-menu input#search-term").focus();
|
|
||||||
} else if (index > -1) {
|
|
||||||
index += down ? 1 : -1;
|
|
||||||
if (index >= 0 && index < results.length) {
|
|
||||||
results[index].focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchInput = document.querySelector("#search-term");
|
|
||||||
if (e.key === "Enter" && e.target === searchInput) {
|
|
||||||
const recentEnterHit =
|
|
||||||
this.state._lastEnterTimestamp &&
|
|
||||||
Date.now() - this.state._lastEnterTimestamp < SECOND_ENTER_MAX_DELAY;
|
|
||||||
|
|
||||||
// same combination as key-enter-escape mixin
|
|
||||||
if (
|
|
||||||
e.ctrlKey ||
|
|
||||||
e.metaKey ||
|
|
||||||
(isiPad() && e.altKey) ||
|
|
||||||
(searchData.typeFilter !== DEFAULT_TYPE_FILTER && recentEnterHit)
|
|
||||||
) {
|
|
||||||
this.fullSearch();
|
|
||||||
} else {
|
|
||||||
searchData.typeFilter = null;
|
|
||||||
this.triggerSearch();
|
|
||||||
}
|
|
||||||
this.state._lastEnterTimestamp = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.target === searchInput && e.key === "Backspace") {
|
|
||||||
if (!searchInput.value) {
|
|
||||||
this.clearTopicContext();
|
|
||||||
this.clearPMInboxContext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerSearch() {
|
|
||||||
searchData.noResults = false;
|
|
||||||
if (SearchHelper.includesTopics()) {
|
|
||||||
if (this.state.inTopicContext) {
|
|
||||||
this.search.set("highlightTerm", searchData.term);
|
|
||||||
}
|
|
||||||
|
|
||||||
searchData.loading = true;
|
|
||||||
cancel(this.state._debouncer);
|
|
||||||
SearchHelper.perform(this);
|
|
||||||
if (this.currentUser) {
|
|
||||||
updateRecentSearches(this.currentUser, searchData.term);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
searchData.loading = false;
|
|
||||||
if (!this.state.inTopicContext) {
|
|
||||||
this.state._debouncer = discourseDebounce(
|
|
||||||
SearchHelper,
|
|
||||||
SearchHelper.perform,
|
|
||||||
this,
|
|
||||||
400
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
moreOfType(type) {
|
|
||||||
searchData.typeFilter = type;
|
|
||||||
this.triggerSearch();
|
|
||||||
},
|
|
||||||
|
|
||||||
searchTermChanged(term, opts = {}) {
|
|
||||||
searchData.typeFilter = opts.searchTopics ? null : DEFAULT_TYPE_FILTER;
|
|
||||||
searchData.term = term;
|
|
||||||
this.triggerSearch();
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerAutocomplete(opts = {}) {
|
|
||||||
if (opts.setTopicContext) {
|
|
||||||
this.sendWidgetAction("setTopicContext");
|
|
||||||
this.state.inTopicContext = true;
|
|
||||||
}
|
|
||||||
this.searchTermChanged(opts.value, { searchTopics: opts.searchTopics });
|
|
||||||
},
|
|
||||||
|
|
||||||
fullSearch() {
|
|
||||||
searchData.loading = false;
|
|
||||||
SearchHelper.cancel();
|
|
||||||
const url = this.fullSearchUrl();
|
|
||||||
if (url) {
|
|
||||||
this.sendWidgetEvent("linkClicked");
|
|
||||||
DiscourseURL.routeTo(url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
searchContext() {
|
|
||||||
if (this.state.inTopicContext || this.state.inPMInboxContext) {
|
|
||||||
return this.search.searchContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createWidget("browser-search-tip", {
|
|
||||||
buildKey: () => "browser-search-tip",
|
|
||||||
tagName: "div.browser-search-tip",
|
|
||||||
|
|
||||||
html() {
|
|
||||||
return [
|
|
||||||
h(
|
|
||||||
"span.tip-label",
|
|
||||||
I18n.t("search.browser_tip", {
|
|
||||||
modifier: translateModKey("Meta"),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
h("span.tip-description", I18n.t("search.browser_tip_description")),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,50 +0,0 @@
|
|||||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
|
||||||
import { test } from "qunit";
|
|
||||||
import {
|
|
||||||
acceptance,
|
|
||||||
count,
|
|
||||||
exists,
|
|
||||||
query,
|
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
|
||||||
|
|
||||||
acceptance("Search - Glimmer - Mobile", function (needs) {
|
|
||||||
needs.mobileView();
|
|
||||||
|
|
||||||
test("search", async function (assert) {
|
|
||||||
await visit("/");
|
|
||||||
|
|
||||||
await click("#search-button");
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
exists("input.full-page-search"),
|
|
||||||
"it shows the full page search form"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(!exists(".search-results .fps-topic"), "no results by default");
|
|
||||||
|
|
||||||
await click(".advanced-filters summary");
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
exists(".advanced-filters[open]"),
|
|
||||||
"it should expand advanced search filters"
|
|
||||||
);
|
|
||||||
|
|
||||||
await fillIn(".search-query", "discourse");
|
|
||||||
await click(".search-cta");
|
|
||||||
|
|
||||||
assert.strictEqual(count(".fps-topic"), 1, "has one post");
|
|
||||||
|
|
||||||
assert.notOk(
|
|
||||||
exists(".advanced-filters[open]"),
|
|
||||||
"it should collapse advanced search filters"
|
|
||||||
);
|
|
||||||
|
|
||||||
await click("#search-button");
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query("input.full-page-search").value,
|
|
||||||
"discourse",
|
|
||||||
"it does not reset input when hitting search icon again"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -47,24 +47,4 @@ acceptance("Search - Mobile", function (needs) {
|
|||||||
"it does not reset input when hitting search icon again"
|
"it does not reset input when hitting search icon again"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Search context in full page search", async function (assert) {
|
|
||||||
await visit("/search?context=tag&context_id=dev&skip_context=true");
|
|
||||||
|
|
||||||
assert.ok(exists(".search-header .search-context"));
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".search-header .search-context input[type='checkbox']").checked,
|
|
||||||
false,
|
|
||||||
"checkbox matches query parameter"
|
|
||||||
);
|
|
||||||
|
|
||||||
await click(".search-header .search-context label");
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".search-header .search-context input[type='checkbox']").checked,
|
|
||||||
true,
|
|
||||||
"checkbox toggling works"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
acceptance("Search - Glimmer - Anonymous", function (needs) {
|
acceptance("Search - Anonymous", function (needs) {
|
||||||
needs.pretender((server, helper) => {
|
needs.pretender((server, helper) => {
|
||||||
server.get("/search/query", (request) => {
|
server.get("/search/query", (request) => {
|
||||||
if (request.queryParams.type_filter === DEFAULT_TYPE_FILTER) {
|
if (request.queryParams.type_filter === DEFAULT_TYPE_FILTER) {
|
||||||
@ -431,7 +431,7 @@ acceptance("Search - Glimmer - Anonymous", function (needs) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptance("Search - Glimmer - Authenticated", function (needs) {
|
acceptance("Search - Authenticated", function (needs) {
|
||||||
needs.user();
|
needs.user();
|
||||||
needs.settings({
|
needs.settings({
|
||||||
log_search_queries: true,
|
log_search_queries: true,
|
||||||
@ -741,7 +741,7 @@ acceptance("Search - Glimmer - Authenticated", function (needs) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptance("Search - Glimmer - with tagging enabled", function (needs) {
|
acceptance("Search - with tagging enabled", function (needs) {
|
||||||
needs.user();
|
needs.user();
|
||||||
needs.settings({ tagging_enabled: true });
|
needs.settings({ tagging_enabled: true });
|
||||||
needs.pretender((server, helper) => {
|
needs.pretender((server, helper) => {
|
||||||
@ -923,7 +923,7 @@ acceptance("Search - Glimmer - with tagging enabled", function (needs) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptance("Search - Glimmer - assistant", function (needs) {
|
acceptance("Search - assistant", function (needs) {
|
||||||
needs.user();
|
needs.user();
|
||||||
needs.pretender((server, helper) => {
|
needs.pretender((server, helper) => {
|
||||||
server.get("/t/2179.json", () => {
|
server.get("/t/2179.json", () => {
|
@ -22,6 +22,7 @@ import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions"
|
|||||||
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
||||||
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
||||||
import { resetItemSelectCallbacks } from "discourse/components/search-menu/results/assistant-item";
|
import { resetItemSelectCallbacks } from "discourse/components/search-menu/results/assistant-item";
|
||||||
|
import { resetQuickSearchRandomTips } from "discourse/components/search-menu/results/random-quick-tip";
|
||||||
import { resetOnKeyUpCallbacks } from "discourse/components/search-menu/search-term";
|
import { resetOnKeyUpCallbacks } from "discourse/components/search-menu/search-term";
|
||||||
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
|
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
|
||||||
import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content";
|
import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content";
|
||||||
@ -84,11 +85,6 @@ import {
|
|||||||
import { clearExtraHeaderIcons } from "discourse/widgets/header";
|
import { clearExtraHeaderIcons } from "discourse/widgets/header";
|
||||||
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
||||||
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
|
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
|
||||||
import {
|
|
||||||
initSearchData,
|
|
||||||
resetOnKeyDownCallbacks,
|
|
||||||
} from "discourse/widgets/search-menu";
|
|
||||||
import { resetQuickSearchRandomTips } from "discourse/widgets/search-menu-results";
|
|
||||||
import { resetDecorators } from "discourse/widgets/widget";
|
import { resetDecorators } from "discourse/widgets/widget";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
|
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
|
||||||
@ -188,7 +184,6 @@ export function testCleanup(container, app) {
|
|||||||
clearOutletCache();
|
clearOutletCache();
|
||||||
clearHTMLCache();
|
clearHTMLCache();
|
||||||
clearRewrites();
|
clearRewrites();
|
||||||
initSearchData();
|
|
||||||
resetDecorators();
|
resetDecorators();
|
||||||
resetPostCookedDecorators();
|
resetPostCookedDecorators();
|
||||||
resetPluginOutletDecorators();
|
resetPluginOutletDecorators();
|
||||||
@ -229,7 +224,6 @@ export function testCleanup(container, app) {
|
|||||||
resetNotificationTypeRenderers();
|
resetNotificationTypeRenderers();
|
||||||
resetSidebarPanels();
|
resetSidebarPanels();
|
||||||
clearExtraHeaderIcons();
|
clearExtraHeaderIcons();
|
||||||
resetOnKeyDownCallbacks();
|
|
||||||
resetOnKeyUpCallbacks();
|
resetOnKeyUpCallbacks();
|
||||||
resetItemSelectCallbacks();
|
resetItemSelectCallbacks();
|
||||||
resetUserMenuTabs();
|
resetUserMenuTabs();
|
||||||
|
@ -10,7 +10,7 @@ import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
|||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
// Note this isn't a full-fledge test of the search menu. Those tests are in
|
// Note this isn't a full-fledge test of the search menu. Those tests are in
|
||||||
// acceptance/glimmer-search-test.js. This is simply about the rendering of the
|
// acceptance/search-test.js. This is simply about the rendering of the
|
||||||
// menu panel separate from the search input.
|
// menu panel separate from the search input.
|
||||||
module("Integration | Component | search-menu", function (hooks) {
|
module("Integration | Component | search-menu", function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
@ -130,11 +130,40 @@ $search-pad-horizontal: 0.5em;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-result-group .group-result,
|
.search-result-group .group-result,
|
||||||
.search-result-user .user-result {
|
.search-result-user {
|
||||||
|
.search-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
img.avatar {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-titles {
|
||||||
|
@include user-item-flex;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username,
|
||||||
|
.name,
|
||||||
|
.custom-field {
|
||||||
|
color: var(--primary-high-or-secondary-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-field {
|
||||||
|
font-size: var(--font-down-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.user-result {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: var(--font-down-1);
|
font-size: var(--font-down-1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.search-result-group .group-result {
|
.search-result-group .group-result {
|
||||||
.d-icon,
|
.d-icon,
|
||||||
.avatar-flair {
|
.avatar-flair {
|
||||||
@ -171,31 +200,6 @@ $search-pad-horizontal: 0.5em;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-user .user-result {
|
|
||||||
.user-titles {
|
|
||||||
@include user-item-flex;
|
|
||||||
|
|
||||||
.username,
|
|
||||||
.name {
|
|
||||||
@include ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username,
|
|
||||||
.name,
|
|
||||||
.custom-field {
|
|
||||||
color: var(--primary-high-or-secondary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-field {
|
|
||||||
font-size: var(--font-down-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-result-category,
|
.search-result-category,
|
||||||
.search-result-tag {
|
.search-result-tag {
|
||||||
+ .search-result-user,
|
+ .search-result-user,
|
||||||
@ -205,11 +209,19 @@ $search-pad-horizontal: 0.5em;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-result-user .user-result img.avatar,
|
.search-result-user .user-result img.avatar,
|
||||||
.search-item-user img.avatar {
|
.search-item-user {
|
||||||
|
display: flex;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
img.avatar {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
.username {
|
||||||
|
margin-right: 0.33em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.label-suffix {
|
.label-suffix {
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
@ -218,6 +230,10 @@ $search-pad-horizontal: 0.5em;
|
|||||||
|
|
||||||
.search-item-tag {
|
.search-item-tag {
|
||||||
color: var(--primary-high);
|
color: var(--primary-high);
|
||||||
|
|
||||||
|
.d-icon {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.extra-hint {
|
.extra-hint {
|
||||||
@ -377,55 +393,3 @@ $search-pad-horizontal: 0.5em;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// these styles will become the default once the glimmer search menu
|
|
||||||
// is enabled for all users, and the old search menu is removed
|
|
||||||
// we can then drop any '!important' rules
|
|
||||||
.search-menu.glimmer-search-menu {
|
|
||||||
.search-item-tag .d-icon {
|
|
||||||
margin-right: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-item-user {
|
|
||||||
display: flex;
|
|
||||||
align-self: center;
|
|
||||||
|
|
||||||
.username {
|
|
||||||
margin-right: 0.33em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-result-user {
|
|
||||||
.search-link {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
img.avatar {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-titles {
|
|
||||||
@include user-item-flex;
|
|
||||||
|
|
||||||
.username,
|
|
||||||
.name {
|
|
||||||
@include ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username,
|
|
||||||
.name,
|
|
||||||
.custom-field {
|
|
||||||
color: var(--primary-high-or-secondary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-field {
|
|
||||||
font-size: var(--font-down-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2323,10 +2323,6 @@ developer:
|
|||||||
experimental_topics_filter:
|
experimental_topics_filter:
|
||||||
client: true
|
client: true
|
||||||
default: false
|
default: false
|
||||||
experimental_search_menu:
|
|
||||||
client: true
|
|
||||||
hidden: true
|
|
||||||
default: true
|
|
||||||
max_sidebar_section_links:
|
max_sidebar_section_links:
|
||||||
default: 50
|
default: 50
|
||||||
hidden: true
|
hidden: true
|
||||||
|
Loading…
Reference in New Issue
Block a user