DEV: Remove legacy hamburger menu code (#24788)

We no longer offer the option to use the legacy hamburger menu since October 9th 2023, see 832b3b9e60. However, the code for the legacy hamburger menu is still around and needs to be removed. All plugins and themes that we know of that customize the legacy hamburger menu have been updated to either remove the customizations or migrate the customizations to the new sidebar, so now we can safely remove the legacy hamburger menu code from core.

Internal topic: t/113137.
This commit is contained in:
Osama Sayegh 2023-12-11 20:17:00 +03:00 committed by GitHub
parent 223e413a6c
commit 102e1b4faf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2 additions and 710 deletions

View File

@ -75,7 +75,6 @@ import {
import { registerCustomTagSectionLinkPrefixIcon } from "discourse/lib/sidebar/user/tags-section/base-tag-section-link";
import { consolePrefix } from "discourse/lib/source-identifier";
import { includeAttributes } from "discourse/lib/transform-post";
import DiscourseURL from "discourse/lib/url";
import { registerUserMenuTab } from "discourse/lib/user-menu/tab";
import { replaceFormatter } from "discourse/lib/utilities";
import { addCardClickListenerSelector } from "discourse/mixins/card-contents-base";
@ -134,7 +133,6 @@ import {
registerIconRenderer,
replaceIcon,
} from "discourse-common/lib/icon-library";
import I18n from "discourse-i18n";
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
import { modifySelectKit } from "select-kit/mixins/plugin-api";
@ -526,52 +524,9 @@ class PluginApi {
*
**/
decorateWidget(name, fn) {
this._deprecateDecoratingHamburgerWidgetLinks(name, fn);
decorateWidget(name, fn);
}
/**
* This is a bridge to support the legacy hamburger widget links that are added by decorating the widgets. This can
* be removed once the legacy hamburger widget no longer exists.
*/
_deprecateDecoratingHamburgerWidgetLinks(name, fn) {
if (
name === "hamburger-menu:generalLinks" ||
name === "hamburger-menu:footerLinks"
) {
deprecated(
`Usage of \`api.decorateWidget('${name}')\` is deprecated, please use \`api.addCommunitySectionLink\` instead.`,
{
id: "discourse.decorate-widget.hamburger-widget-links",
since: "3.2",
dropFrom: "3.3",
}
);
const { href, route, label, rawLabel, className } = fn();
const textContent = rawLabel || I18n.t(label);
const args = {
name: className || textContent.replace(/\s+/g, "-").toLowerCase(),
title: textContent,
text: textContent,
};
if (href) {
if (DiscourseURL.isInternal(href)) {
args.href = href;
} else {
// Skip external links support for now
return;
}
} else {
args.route = route;
}
this.addCommunitySectionLink(args, name.match(/footerLinks/));
}
}
/**
* Adds a new action to a widget that already exists. You can use this to
* add additional functionality from your plugin.

View File

@ -1,79 +0,0 @@
import { h } from "virtual-dom";
import { number } from "discourse/lib/formatter";
import Category from "discourse/models/category";
import { createWidget } from "discourse/widgets/widget";
import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n";
createWidget("hamburger-category", {
tagName: "li.category-link",
html(c) {
if (c.parent_category_id) {
this.tagName += ".subcategory";
}
this.tagName += ".category-" + Category.slugFor(c, "-");
const results = [
this.attach("category-link", { category: c, allowUncategorized: true }),
];
const unreadTotal = c.unreadTopicsCount + c.newTopicsCount;
if (unreadTotal) {
results.push(
h(
"a.badge.badge-notification",
{
attributes: { href: c.get("url") },
},
number(unreadTotal)
)
);
}
if (!this.currentUser) {
let count;
if (c.get("show_subcategory_list")) {
count = c.get("totalTopicCount");
} else {
count = c.get("topic_count");
}
results.push(h("b.topics-count", number(count)));
}
return results;
},
});
export default createWidget("hamburger-categories", {
tagName: "ul.category-links.clearfix",
html(attrs) {
const href = getURL("/categories");
let title = I18n.t("filters.categories.title");
if (attrs.moreCount > 0) {
title = I18n.t("categories.n_more", { count: attrs.moreCount });
}
let result = [
h(
"li.heading",
h("a.d-link.categories-link", { attributes: { href } }, title)
),
];
const categories = attrs.categories;
if (categories.length === 0) {
return;
}
result = result.concat(
categories.map((c) => this.attach("hamburger-category", c))
);
return result;
},
});

View File

@ -1,374 +0,0 @@
import { h } from "virtual-dom";
import { ajax } from "discourse/lib/ajax";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { NotificationLevels } from "discourse/lib/notification-levels";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { applyDecorators, createWidget } from "discourse/widgets/widget";
import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n";
const flatten = (array) => [].concat.apply([], array);
createWidget("priority-faq-link", {
tagName: "a.faq-priority.widget-link",
buildAttributes(attrs) {
return { href: attrs.href };
},
html() {
return [
I18n.t("faq"),
" ",
h("span.badge.badge-notification", I18n.t("new_item")),
];
},
click(e) {
const {
attrs: { href },
currentUser,
siteSettings,
} = this;
if (siteSettings.faq_url === href) {
ajax(userPath("read-faq"), { type: "POST" }).then(() => {
currentUser.set("read_faq", true);
if (wantsNewWindow(e)) {
return;
}
e.preventDefault();
DiscourseURL.routeTo(href);
});
} else {
if (wantsNewWindow(e)) {
return;
}
e.preventDefault();
DiscourseURL.routeTo(href);
}
},
});
export default createWidget("hamburger-menu", {
buildKey: () => "hamburger-menu",
tagName: "div.hamburger-panel",
settings: {
showCategories: true,
maxWidth: 320,
showFAQ: true,
showAbout: true,
},
defaultState() {
return { loaded: false, loading: false };
},
adminLinks() {
const { currentUser } = this;
const links = [
{
route: "admin",
className: "admin-link",
icon: "wrench",
label: "admin_title",
},
];
if (currentUser.admin) {
links.push({
href: "/admin/site_settings",
icon: "cog",
label: "admin.site_settings.title",
className: "settings-link",
});
}
return links.map((l) => this.attach("link", l));
},
lookupCount(type) {
const tts = this.register.lookup("service:topic-tracking-state");
return tts ? tts.lookupCount({ type }) : 0;
},
generalLinks() {
const { attrs, currentUser, siteSettings, state } = this;
const links = [];
links.push({
route: "discovery.latest",
className: "latest-topics-link",
label: "filters.latest.title",
title: "filters.latest.help",
});
if (currentUser) {
links.push({
route: "discovery.new",
className: "new-topics-link",
labelCount: "filters.new.title_with_count",
label: "filters.new.title",
title: "filters.new.help",
count: this.lookupCount("new"),
});
links.push({
route: "discovery.unread",
className: "unread-topics-link",
labelCount: "filters.unread.title_with_count",
label: "filters.unread.title",
title: "filters.unread.help",
count: this.lookupCount("unread"),
});
if (currentUser.can_review) {
links.push({
route: siteSettings.reviewable_default_topics
? "review.topics"
: "review",
className: "review",
label: "review.title",
badgeCount: "reviewable_count",
badgeClass: "reviewables",
});
}
}
links.push({
route: "discovery.top",
className: "top-topics-link",
label: "filters.top.title",
title: "filters.top.help",
});
if (siteSettings.enable_badges) {
links.push({
route: "badges",
className: "badge-link",
label: "badges.title",
});
}
const canSeeUserProfiles =
currentUser || !siteSettings.hide_user_profiles_from_public;
if (siteSettings.enable_user_directory && canSeeUserProfiles) {
links.push({
route: "users",
className: "user-directory-link",
label: "directory.title",
});
}
if (siteSettings.enable_group_directory) {
links.push({
route: "groups",
className: "groups-link",
label: "groups.index.title",
});
}
if (siteSettings.tagging_enabled) {
links.push({ route: "tags", label: "tagging.tags" });
}
const extraLinks = flatten(
applyDecorators(this, "generalLinks", attrs, state)
);
return links.concat(extraLinks).map((l) => this.attach("link", l));
},
listCategories() {
const { currentUser, site, siteSettings } = this;
const maxCategoriesToDisplay = siteSettings.header_dropdown_category_count;
let categories = [];
if (currentUser) {
const allCategories = site
.get("categories")
.filter((c) => c.notification_level !== NotificationLevels.MUTED);
categories = allCategories
.filter((c) => c.newTopicsCount > 0 || c.unreadTopicsCount > 0)
.sort((a, b) => {
return (
b.newTopicsCount +
b.unreadTopicsCount -
(a.unreadTopicsCount + a.newTopicsCount)
);
});
const topCategoryIds = currentUser.get("top_category_ids") || [];
topCategoryIds.forEach((id) => {
const category = allCategories.find((c) => c.id === id);
if (category && !categories.includes(category)) {
categories.push(category);
}
});
categories = categories.concat(
allCategories
.filter((c) => !categories.includes(c))
.sort((a, b) => b.topic_count - a.topic_count)
);
} else {
categories = site
.get("categoriesByCount")
.filter((c) => c.notification_level !== NotificationLevels.MUTED);
}
if (!siteSettings.allow_uncategorized_topics) {
categories = categories.filter(
(c) => c.id !== site.uncategorized_category_id
);
}
const moreCount = categories.length - maxCategoriesToDisplay;
categories = categories.slice(0, maxCategoriesToDisplay);
return this.attach("hamburger-categories", { categories, moreCount });
},
footerLinks(prioritizeFaq, faqUrl) {
const { attrs, capabilities, settings, site, siteSettings, state } = this;
const links = [];
if (settings.showAbout) {
links.push({
route: "about",
className: "about-link",
label: "about.simple_title",
});
}
if (settings.showFAQ && !prioritizeFaq) {
links.push({ href: faqUrl, className: "faq-link", label: "faq" });
}
if (!site.mobileView && !capabilities.touch) {
links.push({
href: "",
action: "showKeyboard",
className: "keyboard-shortcuts-link",
label: "keyboard_shortcuts_help.title",
});
}
const mobileTouch = siteSettings.enable_mobile_theme && capabilities.touch;
if (site.mobileView || mobileTouch) {
links.push({
action: "toggleMobileView",
className: "mobile-toggle-link",
label: site.mobileView ? "desktop_view" : "mobile_view",
});
}
const extraLinks = flatten(
applyDecorators(this, "footerLinks", attrs, state)
);
return links.concat(extraLinks).map((l) => this.attach("link", l));
},
panelContents() {
const { attrs, currentUser, settings, siteSettings, state } = this;
const results = [];
const faqUrl = siteSettings.faq_url || getURL("/faq");
const prioritizeFaq =
settings.showFAQ && currentUser && !currentUser.read_faq;
if (prioritizeFaq) {
results.push(
this.attach("menu-links", {
name: "faq-link",
heading: true,
contents: () => {
return this.attach("priority-faq-link", { href: faqUrl });
},
})
);
}
if (currentUser && currentUser.staff) {
results.push(
this.attach("menu-links", {
name: "admin-links",
contents: () => {
const extraLinks = flatten(
applyDecorators(this, "admin-links", attrs, state)
);
return this.adminLinks().concat(extraLinks);
},
})
);
}
results.push(
this.attach("menu-links", {
name: "general-links",
contents: () => this.generalLinks(),
})
);
if (settings.showCategories) {
results.push(this.listCategories());
results.push(h("hr.categories-separator"));
}
results.push(
this.attach("menu-links", {
name: "footer-links",
omitRule: true,
contents: () => this.footerLinks(prioritizeFaq, faqUrl),
})
);
return results;
},
refreshReviewableCount(state) {
const { currentUser } = this;
if (state.loading || !currentUser || !currentUser.can_review) {
return;
}
state.loading = true;
return ajax("/review/count.json")
.then(({ count }) => currentUser.set("reviewable_count", count))
.finally(() => {
state.loaded = true;
state.loading = false;
this.scheduleRerender();
});
},
html() {
return this.attach("menu-panel", {
contents: () => this.panelContents(),
maxWidth: this.settings.maxWidth,
});
},
clickOutside() {
this.sendWidgetAction("toggleHamburger");
},
keyDown(e) {
if (e.key === "Escape") {
this.sendWidgetAction("toggleHamburger");
e.preventDefault();
return false;
}
},
});

View File

@ -326,7 +326,7 @@ export function attachAdditionalPanel(name, toggle, transformAttrs) {
additionalPanels.push({ name, toggle, transformAttrs });
}
createWidget("revamped-hamburger-menu-wrapper", {
createWidget("hamburger-dropdown-wrapper", {
buildAttributes() {
return { "data-click-outside": true };
},
@ -531,15 +531,7 @@ export default createWidget("header", {
);
}
} else if (state.hamburgerVisible) {
if (
attrs.navigationMenuQueryParamOverride === "header_dropdown" ||
!attrs.sidebarEnabled ||
this.site.narrowDesktopView
) {
panels.push(this.attach("revamped-hamburger-menu-wrapper", {}));
} else {
panels.push(this.attach("hamburger-menu"));
}
panels.push(this.attach("hamburger-dropdown-wrapper", {}));
} else if (state.userVisible) {
panels.push(this.attach("revamped-user-menu-wrapper", {}));
}

View File

@ -1,38 +1,6 @@
import { h } from "virtual-dom";
import hbs from "discourse/widgets/hbs-compiler";
import { createWidget } from "discourse/widgets/widget";
createWidget("menu-links", {
buildClasses(attrs) {
if (attrs.name && attrs.name.length) {
return `menu-container-${attrs.name}`;
}
},
html(attrs) {
const links = [].concat(attrs.contents());
const liOpts = {};
if (attrs.heading) {
liOpts.className = "header";
}
const result = [];
result.push(
h(
"ul.menu-links.columned",
links.map((l) => h("li", liOpts, l))
)
);
result.push(h("div.clearfix"));
if (!attrs.omitRule) {
result.push(h("hr"));
}
return result;
},
});
createWidget("menu-panel", {
tagName: "div.menu-panel",
template: hbs`

View File

@ -466,176 +466,6 @@ acceptance("Sidebar - Plugin API", function (needs) {
);
});
test("API bridge for decorating hamburger-menu widget with footer links", async function (assert) {
withPluginApi(PLUGIN_API_VERSION, (api) => {
api.decorateWidget("hamburger-menu:footerLinks", () => {
return {
route: "discovery.top",
rawLabel: "my top",
className: "my-custom-top",
};
});
});
await visit("/");
await click(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
);
const myCustomTopSectionLink = query(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-content-main .sidebar-section-link[data-link-name='my-custom-top']"
);
assert.ok(
myCustomTopSectionLink,
"adds my custom top section link to community section under the secondary section in the More... links drawer"
);
assert.ok(
myCustomTopSectionLink.href.endsWith("/top"),
"sets the right href attribute for the my custom top section link"
);
assert.strictEqual(
myCustomTopSectionLink.textContent.trim(),
"my top",
"displays the right text for my custom top section link"
);
});
test("API bridge for decorating hamburger-menu widget with general links", async function (assert) {
withPluginApi(PLUGIN_API_VERSION, (api) => {
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.latest",
label: "filters.latest.title",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.unread",
rawLabel: "my unreads",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.top",
rawLabel: "my top",
className: "my-custom-top",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
href: "/c/bug?status=open",
rawLabel: "open bugs",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
href: "/t/internationalization-localization/280",
rawLabel: "my favourite topic",
};
});
});
await visit("/");
const customLatestSectionLink = query(
".sidebar-section[data-section-name='community'] .sidebar-section-link[data-link-name='latest']"
);
assert.ok(
customLatestSectionLink,
"adds custom latest section link to community section"
);
assert.ok(
customLatestSectionLink.href.endsWith("/latest"),
"sets the right href attribute for the custom latest section link"
);
assert.strictEqual(
customLatestSectionLink.textContent.trim(),
I18n.t("filters.latest.title"),
"displays the right text for custom latest section link"
);
await click(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
);
const customUnreadSectionLink = query(
".sidebar-section[data-section-name='community'] .sidebar-section-link[data-link-name='my-unreads']"
);
assert.ok(
customUnreadSectionLink,
"adds custom unread section link to community section"
);
assert.ok(
customUnreadSectionLink.href.endsWith("/unread"),
"sets the right href attribute for the custom unread section link"
);
assert.strictEqual(
customUnreadSectionLink.textContent.trim(),
"my unreads",
"displays the right text for custom unread section link"
);
const customTopSectionLInk = query(
".sidebar-section[data-section-name='community'] .sidebar-section-link[data-link-name='my-custom-top']"
);
assert.ok(
customTopSectionLInk,
"adds custom top section link to community section with right link class"
);
const openBugsSectionLink = query(
".sidebar-section[data-section-name='community'] .sidebar-section-link[data-link-name='open-bugs']"
);
assert.ok(
openBugsSectionLink,
"adds custom open bugs section link to community section with right link class"
);
assert.ok(
openBugsSectionLink.href.endsWith("/c/bug?status=open"),
"sets the right href attribute for the custom open bugs section link"
);
// close more links
await click(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
);
await visit("/t/internationalization-localization/280");
assert.ok(
exists(
".sidebar-section[data-section-name='community'] .sidebar-section-link[data-link-name='my-favourite-topic'].active"
),
"displays my favourite topic custom section link when current route matches the link's route"
);
await visit("/t/short-topic-with-two-posts/54077");
assert.notOk(
exists(
".sidebar-section[data-section-name='community'] .sidebar-section-link-my-favourite-topic.active"
),
"does not display my favourite topic custom section link when current route does not match the link's route"
);
});
test("Section that is not displayed via displaySection", async function (assert) {
withPluginApi(PLUGIN_API_VERSION, (api) => {
api.addSidebarSection((BaseCustomSidebarSection) => {