diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js index 880be3fdc22..41d6acc9e92 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.js +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js @@ -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. diff --git a/app/assets/javascripts/discourse/app/widgets/hamburger-categories.js b/app/assets/javascripts/discourse/app/widgets/hamburger-categories.js deleted file mode 100644 index 25d41f3d9f0..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/hamburger-categories.js +++ /dev/null @@ -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; - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/hamburger-menu.js b/app/assets/javascripts/discourse/app/widgets/hamburger-menu.js deleted file mode 100644 index 9fa7e523330..00000000000 --- a/app/assets/javascripts/discourse/app/widgets/hamburger-menu.js +++ /dev/null @@ -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; - } - }, -}); diff --git a/app/assets/javascripts/discourse/app/widgets/header.js b/app/assets/javascripts/discourse/app/widgets/header.js index 15d075e9a4d..40adcf91a8c 100644 --- a/app/assets/javascripts/discourse/app/widgets/header.js +++ b/app/assets/javascripts/discourse/app/widgets/header.js @@ -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", {})); } diff --git a/app/assets/javascripts/discourse/app/widgets/menu-panel.js b/app/assets/javascripts/discourse/app/widgets/menu-panel.js index 7bdb0912b36..5d6f13858ec 100644 --- a/app/assets/javascripts/discourse/app/widgets/menu-panel.js +++ b/app/assets/javascripts/discourse/app/widgets/menu-panel.js @@ -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` diff --git a/app/assets/javascripts/discourse/tests/acceptance/sidebar-plugin-api-test.js b/app/assets/javascripts/discourse/tests/acceptance/sidebar-plugin-api-test.js index 4a34b24f3c3..e71504b094e 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/sidebar-plugin-api-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/sidebar-plugin-api-test.js @@ -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) => {