mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 11:15:05 +08:00
DEV: Remove the old header widgets code (#28390)
Remove the header widget code. More info can be found in https://meta.discourse.org/t/upcoming-header-changes-preparing-themes-and-plugins/296544
This commit is contained in:
parent
a23773f83d
commit
7c3ad27de6
|
@ -1,558 +0,0 @@
|
|||
import { DEBUG } from "@glimmer/env";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { waitForPromise } from "@ember/test-waiters";
|
||||
import ItsATrap from "@discourse/itsatrap";
|
||||
import MountWidget from "discourse/components/mount-widget";
|
||||
import { topicTitleDecorators } from "discourse/components/topic-title";
|
||||
import scrollLock from "discourse/lib/scroll-lock";
|
||||
import SwipeEvents, {
|
||||
getMaxAnimationTimeMs,
|
||||
shouldCloseMenu,
|
||||
} from "discourse/lib/swipe-events";
|
||||
import { isDocumentRTL } from "discourse/lib/text-direction";
|
||||
import Docking from "discourse/mixins/docking";
|
||||
import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { bind, observes } from "discourse-common/utils/decorators";
|
||||
|
||||
let _menuPanelClassesToForceDropdown = [];
|
||||
|
||||
const SiteHeaderComponent = MountWidget.extend(
|
||||
Docking,
|
||||
RerenderOnDoNotDisturbChange,
|
||||
{
|
||||
widget: "header",
|
||||
docAt: null,
|
||||
dockedHeader: null,
|
||||
_animate: false,
|
||||
_swipeMenuOrigin: "right",
|
||||
_topic: null,
|
||||
_itsatrap: null,
|
||||
_applicationElement: null,
|
||||
_PANEL_WIDTH: 340,
|
||||
_swipeEvents: null,
|
||||
|
||||
@observes(
|
||||
"currentUser.unread_notifications",
|
||||
"currentUser.unread_high_priority_notifications",
|
||||
"currentUser.all_unread_notifications_count",
|
||||
"currentUser.reviewable_count",
|
||||
"currentUser.unseen_reviewable_count",
|
||||
"session.defaultColorSchemeIsDark",
|
||||
"session.darkModeAvailable"
|
||||
)
|
||||
notificationsChanged() {
|
||||
this.queueRerender();
|
||||
},
|
||||
|
||||
@observes("site.narrowDesktopView")
|
||||
narrowDesktopViewChanged() {
|
||||
this.eventDispatched("dom:clean", "header");
|
||||
|
||||
if (this._dropDownHeaderEnabled()) {
|
||||
this.appEvents.on(
|
||||
"sidebar-hamburger-dropdown:rendered",
|
||||
this,
|
||||
"_animateMenu"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_animateOpening(panel, event = null) {
|
||||
const headerCloak = document.querySelector(".header-cloak");
|
||||
let durationMs = getMaxAnimationTimeMs();
|
||||
if (event && this.pxClosed > 0) {
|
||||
durationMs = getMaxAnimationTimeMs(
|
||||
this.pxClosed / Math.abs(event.velocityX)
|
||||
);
|
||||
}
|
||||
const timing = {
|
||||
duration: durationMs,
|
||||
fill: "forwards",
|
||||
easing: "ease-out",
|
||||
};
|
||||
panel.animate([{ transform: `translate3d(0, 0, 0)` }], timing);
|
||||
headerCloak.animate([{ opacity: 1 }], timing);
|
||||
this.pxClosed = null;
|
||||
},
|
||||
|
||||
_animateClosing(event, panel, menuOrigin) {
|
||||
this._animate = true;
|
||||
const headerCloak = document.querySelector(".header-cloak");
|
||||
let durationMs = getMaxAnimationTimeMs();
|
||||
if (event && this.pxClosed > 0) {
|
||||
const distancePx = this._PANEL_WIDTH - this.pxClosed;
|
||||
durationMs = getMaxAnimationTimeMs(
|
||||
distancePx / Math.abs(event.velocityX)
|
||||
);
|
||||
}
|
||||
const timing = {
|
||||
duration: durationMs,
|
||||
fill: "forwards",
|
||||
};
|
||||
|
||||
let endPosition = -this._PANEL_WIDTH; //origin left
|
||||
if (menuOrigin === "right") {
|
||||
endPosition = this._PANEL_WIDTH;
|
||||
}
|
||||
panel
|
||||
.animate([{ transform: `translate3d(${endPosition}px, 0, 0)` }], timing)
|
||||
.finished.then(() => {
|
||||
schedule("afterRender", () => {
|
||||
this.eventDispatched("dom:clean", "header");
|
||||
});
|
||||
});
|
||||
|
||||
headerCloak.animate([{ opacity: 0 }], timing);
|
||||
this.pxClosed = null;
|
||||
},
|
||||
|
||||
_leftMenuClass() {
|
||||
return isDocumentRTL() ? "user-menu" : "hamburger-panel";
|
||||
},
|
||||
|
||||
@bind
|
||||
onSwipeStart(event) {
|
||||
const e = event.detail;
|
||||
const center = e.center;
|
||||
const swipeOverValidElement = document
|
||||
.elementsFromPoint(center.x, center.y)
|
||||
.some(
|
||||
(ele) =>
|
||||
ele.classList.contains("panel-body") ||
|
||||
ele.classList.contains("header-cloak")
|
||||
);
|
||||
if (
|
||||
swipeOverValidElement &&
|
||||
(e.direction === "left" || e.direction === "right")
|
||||
) {
|
||||
this.movingElement = document.querySelector(".menu-panel");
|
||||
this.cloakElement = document.querySelector(".header-cloak");
|
||||
scrollLock(true, document.querySelector(".panel-body"));
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
@bind
|
||||
onSwipeEnd(event) {
|
||||
const e = event.detail;
|
||||
const menuPanels = document.querySelectorAll(".menu-panel");
|
||||
const menuOrigin = this._swipeMenuOrigin;
|
||||
scrollLock(false, document.querySelector(".panel-body"));
|
||||
menuPanels.forEach((panel) => {
|
||||
if (shouldCloseMenu(e, menuOrigin)) {
|
||||
this._animateClosing(e, panel, menuOrigin);
|
||||
} else {
|
||||
this._animateOpening(panel, e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@bind
|
||||
onSwipeCancel() {
|
||||
const menuPanels = document.querySelectorAll(".menu-panel");
|
||||
scrollLock(false, document.querySelector(".panel-body"));
|
||||
menuPanels.forEach((panel) => {
|
||||
this._animateOpening(panel);
|
||||
});
|
||||
},
|
||||
|
||||
@bind
|
||||
onSwipe(event) {
|
||||
const e = event.detail;
|
||||
const panel = this.movingElement;
|
||||
const headerCloak = this.cloakElement;
|
||||
|
||||
//origin left
|
||||
this.pxClosed = Math.max(0, -e.deltaX);
|
||||
let translation = -this.pxClosed;
|
||||
if (this._swipeMenuOrigin === "right") {
|
||||
this.pxClosed = Math.max(0, e.deltaX);
|
||||
translation = this.pxClosed;
|
||||
}
|
||||
panel.animate([{ transform: `translate3d(${translation}px, 0, 0)` }], {
|
||||
fill: "forwards",
|
||||
});
|
||||
headerCloak.animate(
|
||||
[
|
||||
{
|
||||
opacity: (this._PANEL_WIDTH - this.pxClosed) / this._PANEL_WIDTH,
|
||||
},
|
||||
],
|
||||
{ fill: "forwards" }
|
||||
);
|
||||
},
|
||||
|
||||
dockCheck() {
|
||||
const header = this.header;
|
||||
|
||||
if (this.docAt === null) {
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
this.docAt = header.offsetTop;
|
||||
}
|
||||
|
||||
const main = (this._applicationElement ??=
|
||||
document.querySelector(".ember-application"));
|
||||
const offsetTop = main ? main.offsetTop : 0;
|
||||
const offset = window.pageYOffset - offsetTop;
|
||||
if (offset >= this.docAt) {
|
||||
if (!this.dockedHeader) {
|
||||
document.body.classList.add("docked");
|
||||
this.dockedHeader = true;
|
||||
}
|
||||
} else {
|
||||
if (this.dockedHeader) {
|
||||
document.body.classList.remove("docked");
|
||||
this.dockedHeader = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setTopic() {
|
||||
const header = getOwner(this).lookup("service:header");
|
||||
if (header.topicInfoVisible) {
|
||||
this._topic = header.topicInfo;
|
||||
} else {
|
||||
this._topic = null;
|
||||
}
|
||||
this.eventDispatched("dom:clean", "header");
|
||||
this.queueRerender();
|
||||
},
|
||||
|
||||
willRender() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get("currentUser.staff")) {
|
||||
document.body.classList.add("staff");
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this._resizeDiscourseMenuPanel = () => this.afterRender();
|
||||
window.addEventListener("resize", this._resizeDiscourseMenuPanel);
|
||||
|
||||
const headerService = getOwner(this).lookup("service:header");
|
||||
headerService.addObserver("topicInfoVisible", this, "setTopic");
|
||||
this.setTopic();
|
||||
|
||||
this.appEvents.on("user-menu:rendered", this, "_animateMenu");
|
||||
|
||||
if (this._dropDownHeaderEnabled()) {
|
||||
this.appEvents.on(
|
||||
"sidebar-hamburger-dropdown:rendered",
|
||||
this,
|
||||
"_animateMenu"
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch("notifications:changed", "user-notifications");
|
||||
this.dispatch("header:keyboard-trigger", "header");
|
||||
this.dispatch("user-menu:navigation", "user-menu");
|
||||
|
||||
this.appEvents.on("dom:clean", this, "_cleanDom");
|
||||
|
||||
if (this.currentUser) {
|
||||
this.currentUser.on("status-changed", this, "queueRerender");
|
||||
}
|
||||
|
||||
const header = document.querySelector("header.d-header");
|
||||
this._itsatrap = new ItsATrap(header);
|
||||
const dirs = ["up", "down"];
|
||||
this._itsatrap.bind(dirs, (e) => this._handleArrowKeysNav(e));
|
||||
},
|
||||
|
||||
_handleArrowKeysNav(event) {
|
||||
const activeTab = document.querySelector(
|
||||
".menu-tabs-container .btn.active"
|
||||
);
|
||||
if (activeTab) {
|
||||
let activeTabNumber = Number(
|
||||
document.activeElement.dataset.tabNumber ||
|
||||
activeTab.dataset.tabNumber
|
||||
);
|
||||
const maxTabNumber =
|
||||
document.querySelectorAll(".menu-tabs-container .btn").length - 1;
|
||||
const isNext = event.key === "ArrowDown";
|
||||
let nextTab = isNext ? activeTabNumber + 1 : activeTabNumber - 1;
|
||||
if (isNext && nextTab > maxTabNumber) {
|
||||
nextTab = 0;
|
||||
}
|
||||
if (!isNext && nextTab < 0) {
|
||||
nextTab = maxTabNumber;
|
||||
}
|
||||
event.preventDefault();
|
||||
document
|
||||
.querySelector(
|
||||
`.menu-tabs-container .btn[data-tab-number='${nextTab}']`
|
||||
)
|
||||
.focus();
|
||||
}
|
||||
},
|
||||
|
||||
_cleanDom() {
|
||||
// For performance, only trigger a re-render if any menu panels are visible
|
||||
if (this.element.querySelector(".menu-panel")) {
|
||||
this.eventDispatched("dom:clean", "header");
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
window.removeEventListener("resize", this._resizeDiscourseMenuPanel);
|
||||
getOwner(this)
|
||||
.lookup("service:header")
|
||||
.removeObserver("topicInfoVisible", this, "setTopic");
|
||||
this.appEvents.off("dom:clean", this, "_cleanDom");
|
||||
this.appEvents.off("user-menu:rendered", this, "_animateMenu");
|
||||
|
||||
if (this._dropDownHeaderEnabled()) {
|
||||
this.appEvents.off(
|
||||
"sidebar-hamburger-dropdown:rendered",
|
||||
this,
|
||||
"_animateMenu"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.currentUser) {
|
||||
this.currentUser.off("status-changed", this, "queueRerender");
|
||||
}
|
||||
|
||||
this._itsatrap?.destroy();
|
||||
this._itsatrap = null;
|
||||
},
|
||||
|
||||
buildArgs() {
|
||||
return {
|
||||
topic: this._topic,
|
||||
canSignUp: this.canSignUp,
|
||||
sidebarEnabled: this.sidebarEnabled,
|
||||
showSidebar: this.showSidebar,
|
||||
navigationMenuQueryParamOverride: this.navigationMenuQueryParamOverride,
|
||||
};
|
||||
},
|
||||
|
||||
afterRender() {
|
||||
const headerTitle = document.querySelector(".header-title .topic-link");
|
||||
if (headerTitle && this._topic) {
|
||||
topicTitleDecorators.forEach((cb) =>
|
||||
cb(this._topic, headerTitle, "header-title")
|
||||
);
|
||||
}
|
||||
this._animateMenu();
|
||||
},
|
||||
|
||||
_animateMenu() {
|
||||
const menuPanels = document.querySelectorAll(".menu-panel");
|
||||
|
||||
if (menuPanels.length === 0) {
|
||||
this._animate = this.site.mobileView || this.site.narrowDesktopView;
|
||||
return;
|
||||
}
|
||||
|
||||
let viewMode =
|
||||
this.site.mobileView || this.site.narrowDesktopView
|
||||
? "slide-in"
|
||||
: "drop-down";
|
||||
|
||||
menuPanels.forEach((panel) => {
|
||||
if (menuPanelContainsClass(panel)) {
|
||||
viewMode = "drop-down";
|
||||
this._animate = false;
|
||||
}
|
||||
|
||||
const headerCloak = document.querySelector(".header-cloak");
|
||||
|
||||
panel.classList.remove("drop-down");
|
||||
panel.classList.remove("slide-in");
|
||||
panel.classList.add(viewMode);
|
||||
|
||||
if (this._animate) {
|
||||
let animationFinished = null;
|
||||
let finalPosition = this._PANEL_WIDTH;
|
||||
this._swipeMenuOrigin = "right";
|
||||
if (
|
||||
(this.site.mobileView || this.site.narrowDesktopView) &&
|
||||
panel.parentElement.classList.contains(this._leftMenuClass())
|
||||
) {
|
||||
this._swipeMenuOrigin = "left";
|
||||
finalPosition = -this._PANEL_WIDTH;
|
||||
}
|
||||
animationFinished = panel.animate(
|
||||
[{ transform: `translate3d(${finalPosition}px, 0, 0)` }],
|
||||
{
|
||||
fill: "forwards",
|
||||
}
|
||||
).finished;
|
||||
|
||||
if (isTesting()) {
|
||||
waitForPromise(animationFinished);
|
||||
}
|
||||
|
||||
headerCloak.animate([{ opacity: 0 }], { fill: "forwards" });
|
||||
headerCloak.style.display = "block";
|
||||
|
||||
animationFinished.then(() => {
|
||||
if (isTesting()) {
|
||||
this._animateOpening(panel);
|
||||
} else {
|
||||
discourseLater(() => this._animateOpening(panel));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._animate = false;
|
||||
});
|
||||
},
|
||||
|
||||
_dropDownHeaderEnabled() {
|
||||
return !this.sidebarEnabled || this.site.narrowDesktopView;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function menuPanelContainsClass(menuPanel) {
|
||||
if (!_menuPanelClassesToForceDropdown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if any of the classNames are present in the node's classList
|
||||
for (let className of _menuPanelClassesToForceDropdown) {
|
||||
if (menuPanel.classList.contains(className)) {
|
||||
// Found a matching class
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching class found
|
||||
return false;
|
||||
}
|
||||
|
||||
export function forceDropdownForMenuPanels(classNames) {
|
||||
// If classNames is a string, convert it to an array
|
||||
if (typeof classNames === "string") {
|
||||
classNames = [classNames];
|
||||
}
|
||||
return _menuPanelClassesToForceDropdown.push(...classNames);
|
||||
}
|
||||
|
||||
export default SiteHeaderComponent.extend({
|
||||
classNames: ["d-header-wrap"],
|
||||
classNameBindings: ["site.mobileView::drop-down-mode"],
|
||||
headerWrap: null,
|
||||
header: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this._resizeObserver = null;
|
||||
},
|
||||
|
||||
@bind
|
||||
updateHeaderOffset() {
|
||||
// Safari likes overscolling the page (on both iOS and macOS).
|
||||
// This shows up as a negative value in window.scrollY.
|
||||
// We can use this to offset the headerWrap's top offset to avoid
|
||||
// jitteriness and bad positioning.
|
||||
const windowOverscroll = Math.min(0, window.scrollY);
|
||||
|
||||
// The headerWrap's top offset can also be a negative value on Safari,
|
||||
// because of the changing height of the viewport (due to the URL bar).
|
||||
// For our use case, it's best to ensure this is clamped to 0.
|
||||
const headerWrapTop = Math.max(
|
||||
0,
|
||||
Math.floor(this.headerWrap.getBoundingClientRect().top)
|
||||
);
|
||||
let offsetTop = headerWrapTop + windowOverscroll;
|
||||
|
||||
if (DEBUG && isTesting()) {
|
||||
offsetTop -= document
|
||||
.getElementById("ember-testing-container")
|
||||
.getBoundingClientRect().top;
|
||||
|
||||
offsetTop -= 1; // For 1px border on testing container
|
||||
}
|
||||
|
||||
const documentStyle = document.documentElement.style;
|
||||
|
||||
const currentValue =
|
||||
parseInt(documentStyle.getPropertyValue("--header-offset"), 10) || 0;
|
||||
const newValue = this.headerWrap.offsetHeight + offsetTop;
|
||||
|
||||
if (currentValue !== newValue) {
|
||||
documentStyle.setProperty("--header-offset", `${newValue}px`);
|
||||
}
|
||||
},
|
||||
|
||||
@bind
|
||||
onScroll() {
|
||||
schedule("afterRender", this.updateHeaderOffset);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.appEvents.on("site-header:force-refresh", this, "queueRerender");
|
||||
|
||||
this.headerWrap = document.querySelector(".d-header-wrap");
|
||||
|
||||
if (this.headerWrap) {
|
||||
schedule("afterRender", () => {
|
||||
this.header = this.headerWrap.querySelector("header.d-header");
|
||||
this.updateHeaderOffset();
|
||||
const headerTop = this.header.offsetTop;
|
||||
document.documentElement.style.setProperty(
|
||||
"--header-top",
|
||||
`${headerTop}px`
|
||||
);
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", this.onScroll, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
this._resizeObserver = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
if (entry.contentRect) {
|
||||
const headerTop = this.header?.offsetTop;
|
||||
document.documentElement.style.setProperty(
|
||||
"--header-top",
|
||||
`${headerTop}px`
|
||||
);
|
||||
this.updateHeaderOffset();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._resizeObserver.observe(this.headerWrap);
|
||||
|
||||
this._swipeEvents = new SwipeEvents(this.element);
|
||||
if (this.site.mobileView) {
|
||||
this._swipeEvents.addTouchListeners();
|
||||
this.element.addEventListener("swipestart", this.onSwipeStart);
|
||||
this.element.addEventListener("swipeend", this.onSwipeEnd);
|
||||
this.element.addEventListener("swipecancel", this.onSwipeCancel);
|
||||
this.element.addEventListener("swipe", this.onSwipe);
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
window.removeEventListener("scroll", this.onScroll);
|
||||
this._resizeObserver?.disconnect();
|
||||
this.appEvents.off("site-header:force-refresh", this, "queueRerender");
|
||||
if (this.site.mobileView) {
|
||||
this.element.removeEventListener("swipestart", this.onSwipeStart);
|
||||
this.element.removeEventListener("swipeend", this.onSwipeEnd);
|
||||
this.element.removeEventListener("swipecancel", this.onSwipeCancel);
|
||||
this.element.removeEventListener("swipe", this.onSwipe);
|
||||
this._swipeEvents.removeTouchListeners();
|
||||
}
|
||||
},
|
||||
});
|
|
@ -23,9 +23,7 @@ import { forceDropdownForMenuPanels as glimmerForceDropdownForMenuPanels } from
|
|||
import { addGlobalNotice } from "discourse/components/global-notice";
|
||||
import { headerButtonsDAG } from "discourse/components/header";
|
||||
import { headerIconsDAG } from "discourse/components/header/icons";
|
||||
import MountWidget, {
|
||||
addWidgetCleanCallback,
|
||||
} from "discourse/components/mount-widget";
|
||||
import { addWidgetCleanCallback } from "discourse/components/mount-widget";
|
||||
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
|
||||
import {
|
||||
addPluginReviewableParam,
|
||||
|
@ -40,7 +38,6 @@ import {
|
|||
} from "discourse/components/search-menu/results/random-quick-tip";
|
||||
import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term";
|
||||
import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section";
|
||||
import { forceDropdownForMenuPanels } from "discourse/components/site-header";
|
||||
import { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant";
|
||||
import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container";
|
||||
import { addTopicTitleDecorator } from "discourse/components/topic-title";
|
||||
|
@ -127,7 +124,6 @@ import {
|
|||
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
||||
import { setNotificationsLimit } from "discourse/routes/user-notifications";
|
||||
import { addComposerSaveErrorCallback } from "discourse/services/composer";
|
||||
import { attachAdditionalPanel } from "discourse/widgets/header";
|
||||
import { addPostClassesCallback } from "discourse/widgets/post";
|
||||
import { addDecorator } from "discourse/widgets/post-cooked";
|
||||
import {
|
||||
|
@ -165,20 +161,6 @@ import { addImageWrapperButton } from "discourse-markdown-it/features/image-cont
|
|||
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
|
||||
import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
||||
|
||||
const DEPRECATED_HEADER_WIDGETS = [
|
||||
"header",
|
||||
"site-header",
|
||||
"header-contents",
|
||||
"header-buttons",
|
||||
"user-status-bubble",
|
||||
"sidebar-toggle",
|
||||
"header-icons",
|
||||
"header-topic-info",
|
||||
"header-notifications",
|
||||
"home-logo",
|
||||
"user-dropdown",
|
||||
];
|
||||
|
||||
const appliedModificationIds = new WeakMap();
|
||||
|
||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||
|
@ -732,7 +714,7 @@ class PluginApi {
|
|||
**/
|
||||
decorateWidget(name, fn) {
|
||||
const widgetName = name.split(":")[0];
|
||||
this.#deprecatedHeaderWidgetOverride(widgetName, "decorateWidget");
|
||||
this.#deprecatedWidgetOverride(widgetName, "decorateWidget");
|
||||
|
||||
decorateWidget(name, fn);
|
||||
}
|
||||
|
@ -763,7 +745,7 @@ class PluginApi {
|
|||
return;
|
||||
}
|
||||
|
||||
this.#deprecatedHeaderWidgetOverride(widget, "attachWidgetAction");
|
||||
this.#deprecatedWidgetOverride(widget, "attachWidgetAction");
|
||||
|
||||
widgetClass.prototype[actionName] = fn;
|
||||
}
|
||||
|
@ -1108,7 +1090,7 @@ class PluginApi {
|
|||
*
|
||||
**/
|
||||
changeWidgetSetting(widgetName, settingName, newValue) {
|
||||
this.#deprecatedHeaderWidgetOverride(widgetName, "changeWidgetSetting");
|
||||
this.#deprecatedWidgetOverride(widgetName, "changeWidgetSetting");
|
||||
changeSetting(widgetName, settingName, newValue);
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1124,7 @@ class PluginApi {
|
|||
**/
|
||||
|
||||
reopenWidget(name, args) {
|
||||
this.#deprecatedHeaderWidgetOverride(name, "reopenWidget");
|
||||
this.#deprecatedWidgetOverride(name, "reopenWidget");
|
||||
return reopenWidget(name, args);
|
||||
}
|
||||
|
||||
|
@ -1169,16 +1151,12 @@ class PluginApi {
|
|||
* and returns a hash of values to pass to attach
|
||||
*
|
||||
**/
|
||||
addHeaderPanel(name, toggle, transformAttrs) {
|
||||
deprecated(
|
||||
"addHeaderPanel will be removed as part of the glimmer header upgrade. Use api.headerIcons instead.",
|
||||
{
|
||||
id: "discourse.add-header-panel",
|
||||
url: "https://meta.discourse.org/t/296544",
|
||||
}
|
||||
addHeaderPanel() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
consolePrefix(),
|
||||
`api.addHeaderPanel: This API was decommissioned. Use api.headerIcons instead.`
|
||||
);
|
||||
this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
|
||||
attachAdditionalPanel(name, toggle, transformAttrs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2141,19 +2119,12 @@ class PluginApi {
|
|||
* ```
|
||||
*
|
||||
**/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
addToHeaderIcons(icon) {
|
||||
deprecated(
|
||||
"addToHeaderIcons has been deprecated. Use api.headerIcons instead.",
|
||||
{
|
||||
id: "discourse.add-header-icons",
|
||||
url: "https://meta.discourse.org/t/296544",
|
||||
}
|
||||
);
|
||||
|
||||
this.headerIcons.add(
|
||||
icon,
|
||||
<template><MountWidget @widget={{icon}} /></template>,
|
||||
{ before: "search" }
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
consolePrefix(),
|
||||
`api.addToHeaderIcons: This API was decommissioned. Use api.headerIcons instead.`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2397,7 +2368,6 @@ class PluginApi {
|
|||
*
|
||||
*/
|
||||
forceDropdownForMenuPanels(classNames) {
|
||||
forceDropdownForMenuPanels(classNames);
|
||||
glimmerForceDropdownForMenuPanels(classNames);
|
||||
}
|
||||
|
||||
|
@ -3282,18 +3252,20 @@ class PluginApi {
|
|||
addLegacyAboutPageStat(name);
|
||||
}
|
||||
|
||||
#deprecatedHeaderWidgetOverride(widgetName, override) {
|
||||
if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) {
|
||||
this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
|
||||
deprecated(
|
||||
`The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
|
||||
{
|
||||
since: "v3.3.0.beta1-dev",
|
||||
id: "discourse.header-widget-overrides",
|
||||
url: "https://meta.discourse.org/t/316549",
|
||||
}
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
#deprecatedWidgetOverride(widgetName, override) {
|
||||
// insert here the code to handle widget deprecations, e.g. for the header widgets we used:
|
||||
// if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) {
|
||||
// this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
|
||||
// deprecated(
|
||||
// `The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
|
||||
// {
|
||||
// since: "v3.3.0.beta1-dev",
|
||||
// id: "discourse.header-widget-overrides",
|
||||
// url: "https://meta.discourse.org/t/316549",
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ export default class Header extends Service {
|
|||
|
||||
@tracked hamburgerVisible = false;
|
||||
@tracked userVisible = false;
|
||||
@tracked anyWidgetHeaderOverrides = false;
|
||||
|
||||
#hiders = new TrackedMap();
|
||||
|
||||
|
@ -72,32 +71,6 @@ export default class Header extends Service {
|
|||
return true;
|
||||
}
|
||||
|
||||
get useGlimmerHeader() {
|
||||
if (this.siteSettings.glimmer_header_mode === "disabled") {
|
||||
return false;
|
||||
} else if (this.siteSettings.glimmer_header_mode === "enabled") {
|
||||
if (this.anyWidgetHeaderOverrides) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Using modern 'glimmer' header, even though there are themes and/or plugins using deprecated APIs. Deprecated header customizations will be broken. (glimmer_header_mode: enabled) https://meta.discourse.org/t/316549"
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Auto
|
||||
if (this.anyWidgetHeaderOverrides) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
"Using legacy 'widget' header because themes and/or plugins are using deprecated APIs. (glimmer_header_mode: auto) https://meta.discourse.org/t/316549"
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerHider(ref, buttons) {
|
||||
const validButtons = buttons
|
||||
.map((button) => {
|
||||
|
|
|
@ -12,32 +12,17 @@
|
|||
/>
|
||||
|
||||
{{#if this.showSiteHeader}}
|
||||
{{#if this.header.useGlimmerHeader}}
|
||||
<GlimmerSiteHeader
|
||||
@canSignUp={{this.canSignUp}}
|
||||
@showCreateAccount={{route-action "showCreateAccount"}}
|
||||
@showLogin={{route-action "showLogin"}}
|
||||
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
||||
@toggleMobileView={{route-action "toggleMobileView"}}
|
||||
@logout={{route-action "logout"}}
|
||||
@sidebarEnabled={{this.sidebarEnabled}}
|
||||
@showSidebar={{this.showSidebar}}
|
||||
@toggleSidebar={{this.toggleSidebar}}
|
||||
/>
|
||||
{{else}}
|
||||
<SiteHeader
|
||||
@canSignUp={{this.canSignUp}}
|
||||
@showCreateAccount={{route-action "showCreateAccount"}}
|
||||
@showLogin={{route-action "showLogin"}}
|
||||
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
||||
@toggleMobileView={{route-action "toggleMobileView"}}
|
||||
@logout={{route-action "logout"}}
|
||||
@sidebarEnabled={{this.sidebarEnabled}}
|
||||
@navigationMenuQueryParamOverride={{this.navigationMenuQueryParamOverride}}
|
||||
@showSidebar={{this.showSidebar}}
|
||||
@toggleSidebar={{action "toggleSidebar"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<GlimmerSiteHeader
|
||||
@canSignUp={{this.canSignUp}}
|
||||
@showCreateAccount={{route-action "showCreateAccount"}}
|
||||
@showLogin={{route-action "showLogin"}}
|
||||
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
||||
@toggleMobileView={{route-action "toggleMobileView"}}
|
||||
@logout={{route-action "logout"}}
|
||||
@sidebarEnabled={{this.sidebarEnabled}}
|
||||
@showSidebar={{this.showSidebar}}
|
||||
@toggleSidebar={{this.toggleSidebar}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<SoftwareUpdatePrompt />
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"after-header-panel-outlet",
|
||||
"div.after-header-panel-outlet",
|
||||
hbs`<PluginOutlet @name="after-header-panel" @outletArgs={{hash topic=@data.topic}} /> `
|
||||
);
|
|
@ -1,8 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"before-header-logo-outlet",
|
||||
"div.before-header-logo-outlet",
|
||||
hbs`<PluginOutlet @name="before-header-logo" @outletArgs={{hash attrs=@data}} /> `
|
||||
);
|
|
@ -1,8 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"before-header-panel-outlet",
|
||||
"div.before-header-panel-outlet",
|
||||
hbs`<PluginOutlet @name="before-header-panel" @outletArgs={{hash topic=@data.topic}} /> `
|
||||
);
|
|
@ -1,8 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"header-bootstrap-mode",
|
||||
"div.d-header-mode",
|
||||
hbs`<BootstrapModeNotice />`
|
||||
);
|
|
@ -1,36 +0,0 @@
|
|||
import hbs from "discourse/widgets/hbs-compiler";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
|
||||
createWidget("header-contents", {
|
||||
tagName: "div.contents",
|
||||
transform() {
|
||||
return {
|
||||
showBootstrapMode: this.currentUser?.staff && this.site.desktopView,
|
||||
};
|
||||
},
|
||||
template: hbs`
|
||||
{{#if this.site.desktopView}}
|
||||
{{#if attrs.sidebarEnabled}}
|
||||
{{sidebar-toggle attrs=attrs}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{before-header-logo-outlet attrs=attrs}}
|
||||
|
||||
{{home-logo-wrapper-outlet attrs=attrs}}
|
||||
|
||||
{{#if attrs.topic}}
|
||||
{{header-topic-info attrs=attrs}}
|
||||
{{else if this.siteSettings.bootstrap_mode_enabled}}
|
||||
{{#if transformed.showBootstrapMode}}
|
||||
{{header-bootstrap-mode attrs=attrs}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{before-header-panel-outlet attrs=attrs}}
|
||||
|
||||
<div class="panel" role="navigation">{{yield}}</div>
|
||||
|
||||
{{after-header-panel-outlet attrs=attrs}}
|
||||
`,
|
||||
});
|
|
@ -1,260 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { h } from "virtual-dom";
|
||||
import renderTags from "discourse/lib/render-tags";
|
||||
import { topicFeaturedLinkNode } from "discourse/lib/render-topic-featured-link";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { avatarImg } from "discourse/widgets/post";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import RenderGlimmer from "discourse/widgets/render-glimmer";
|
||||
import { applyDecorators, createWidget } from "discourse/widgets/widget";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
createWidget("topic-header-participant", {
|
||||
tagName: "span",
|
||||
|
||||
buildClasses(attrs) {
|
||||
return `trigger-${attrs.type}-card`;
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
const { user, group } = attrs;
|
||||
let content, url;
|
||||
|
||||
if (attrs.type === "user") {
|
||||
content = avatarImg("tiny", {
|
||||
template: user.avatar_template,
|
||||
username: user.username,
|
||||
});
|
||||
url = user.get("path");
|
||||
} else {
|
||||
content = [iconNode("users")];
|
||||
url = getURL(`/g/${group.name}`);
|
||||
content.push(h("span", group.name));
|
||||
}
|
||||
|
||||
return h(
|
||||
"a.icon",
|
||||
{
|
||||
attributes: {
|
||||
href: url,
|
||||
"data-auto-route": true,
|
||||
title: attrs.username,
|
||||
},
|
||||
},
|
||||
content
|
||||
);
|
||||
},
|
||||
|
||||
click(e) {
|
||||
this.appEvents.trigger(
|
||||
`topic-header:trigger-${this.attrs.type}-card`,
|
||||
this.attrs.username,
|
||||
e.target,
|
||||
e
|
||||
);
|
||||
e.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
export default createWidget("header-topic-info", {
|
||||
tagName: "div.extra-info-wrapper",
|
||||
contents: null,
|
||||
title: null,
|
||||
|
||||
buildClasses(attrs, state) {
|
||||
this.buildAttributes(attrs, state);
|
||||
return this.containerClassName();
|
||||
},
|
||||
|
||||
buildFancyTitleClass() {
|
||||
const baseClass = ["topic-link"];
|
||||
const flatten = (array) => [].concat.apply([], array);
|
||||
const extraClass = flatten(
|
||||
applyDecorators(this, "fancyTitleClass", this.attrs, this.state)
|
||||
);
|
||||
return baseClass.concat(extraClass).filter(Boolean).join(" ");
|
||||
},
|
||||
|
||||
buildAttributes(attrs, state) {
|
||||
const topic = attrs.topic;
|
||||
|
||||
const heading = [];
|
||||
|
||||
const showPM = !topic.get("is_warning") && topic.get("isPrivateMessage");
|
||||
if (showPM) {
|
||||
const href = this.currentUser && this.currentUser.pmPath(topic);
|
||||
if (href) {
|
||||
heading.push(
|
||||
h(
|
||||
"a.private-message-glyph-wrapper",
|
||||
{
|
||||
attributes: { href, "aria-label": I18n.t("user.messages.inbox") },
|
||||
},
|
||||
iconNode("envelope", { class: "private-message-glyph" })
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
const loaded = topic.get("details.loaded");
|
||||
const fancyTitle = topic.get("fancyTitle");
|
||||
const href = topic.get("url");
|
||||
|
||||
if (fancyTitle && href) {
|
||||
heading.push(this.attach("topic-status", attrs));
|
||||
|
||||
const titleHTML = new RawHtml({ html: `<span>${fancyTitle}</span>` });
|
||||
heading.push(
|
||||
this.attach("link", {
|
||||
className: this.buildFancyTitleClass(),
|
||||
action: "jumpToTopPost",
|
||||
href,
|
||||
attributes: { "data-topic-id": topic.get("id") },
|
||||
contents: () => titleHTML,
|
||||
})
|
||||
);
|
||||
|
||||
heading.push(
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"span.header-topic-title-suffix",
|
||||
hbs`<PluginOutlet @name="header-topic-title-suffix" @outletArgs={{@data.outletArgs}}/>`,
|
||||
{
|
||||
outletArgs: {
|
||||
topic,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.headerElements = [h("h1.header-title", heading)];
|
||||
const category = topic.get("category");
|
||||
|
||||
if (loaded || category) {
|
||||
if (
|
||||
category &&
|
||||
(!category.isUncategorizedCategory ||
|
||||
!this.siteSettings.suppress_uncategorized_badge)
|
||||
) {
|
||||
const parentCategory = category.get("parentCategory");
|
||||
const categories = [];
|
||||
if (parentCategory) {
|
||||
if (
|
||||
this.siteSettings.max_category_nesting > 2 &&
|
||||
this.site.desktopView
|
||||
) {
|
||||
const grandParentCategory = parentCategory.get("parentCategory");
|
||||
if (grandParentCategory) {
|
||||
categories.push(
|
||||
this.attach("category-link", { category: grandParentCategory })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
categories.push(
|
||||
this.attach("category-link", { category: parentCategory })
|
||||
);
|
||||
}
|
||||
categories.push(
|
||||
this.attach("category-link", { category, hideParent: true })
|
||||
);
|
||||
|
||||
this.headerElements.push(h("div.categories-wrapper", categories));
|
||||
}
|
||||
|
||||
let extra = [];
|
||||
const tags = renderTags(topic);
|
||||
if (tags && tags.length > 0) {
|
||||
extra.push(new RawHtml({ html: tags }));
|
||||
}
|
||||
|
||||
if (showPM) {
|
||||
const maxHeaderParticipants = extra.length > 0 ? 5 : 10;
|
||||
const participants = [];
|
||||
const topicDetails = topic.get("details");
|
||||
const totalParticipants =
|
||||
topicDetails.allowed_users.length +
|
||||
topicDetails.allowed_groups.length;
|
||||
|
||||
topicDetails.allowed_users.some((user) => {
|
||||
if (participants.length >= maxHeaderParticipants) {
|
||||
return true;
|
||||
}
|
||||
|
||||
participants.push(
|
||||
this.attach("topic-header-participant", {
|
||||
type: "user",
|
||||
user,
|
||||
username: user.username,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
topicDetails.allowed_groups.some((group) => {
|
||||
if (participants.length >= maxHeaderParticipants) {
|
||||
return true;
|
||||
}
|
||||
|
||||
participants.push(
|
||||
this.attach("topic-header-participant", {
|
||||
type: "group",
|
||||
group,
|
||||
username: group.name,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
if (totalParticipants > maxHeaderParticipants) {
|
||||
const remaining = totalParticipants - maxHeaderParticipants;
|
||||
participants.push(
|
||||
this.attach("link", {
|
||||
className: "more-participants",
|
||||
action: "jumpToTopPost",
|
||||
href,
|
||||
attributes: { "data-topic-id": topic.get("id") },
|
||||
contents: () => `+${remaining}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
extra.push(h("div.topic-header-participants", participants));
|
||||
}
|
||||
|
||||
extra = extra.concat(applyDecorators(this, "after-tags", attrs, state));
|
||||
|
||||
if (this.siteSettings.topic_featured_link_enabled) {
|
||||
const featured = topicFeaturedLinkNode(attrs.topic);
|
||||
if (featured) {
|
||||
extra.push(featured);
|
||||
}
|
||||
}
|
||||
if (extra.length) {
|
||||
this.headerElements.push(h("div.topic-header-extra", extra));
|
||||
}
|
||||
}
|
||||
this.contents = h("div.title-wrapper", this.headerElements);
|
||||
},
|
||||
|
||||
html() {
|
||||
return h(
|
||||
"div.extra-info",
|
||||
{ className: this.containerClassName() },
|
||||
this.contents
|
||||
);
|
||||
},
|
||||
|
||||
containerClassName() {
|
||||
return this.headerElements.length > 1 ? "two-rows" : "";
|
||||
},
|
||||
|
||||
jumpToTopPost() {
|
||||
const topic = this.attrs.topic;
|
||||
if (topic) {
|
||||
DiscourseURL.routeTo(topic.get("firstPostUrl"), {
|
||||
keepFilter: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"header-user-tip-shim",
|
||||
"div.header-user-tip-shim",
|
||||
hbs`<UserTip @id="first_notification" @triggerSelector=".header-dropdown-toggle.current-user" @placement="bottom-end" @titleText={{i18n "user_tips.first_notification.title"}} @contentText={{i18n "user_tips.first_notification.content"}} @showSkipButton={{true}} />`
|
||||
);
|
|
@ -1,735 +0,0 @@
|
|||
import { schedule } from "@ember/runloop";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import $ from "jquery";
|
||||
import { h } from "virtual-dom";
|
||||
import { headerButtonsDAG } from "discourse/components/header";
|
||||
import { headerIconsDAG } from "discourse/components/header/icons";
|
||||
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import scrollLock from "discourse/lib/scroll-lock";
|
||||
import { isDocumentRTL } from "discourse/lib/text-direction";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { scrollTop } from "discourse/mixins/scroll-top";
|
||||
import { avatarImg } from "discourse/widgets/post";
|
||||
import RenderGlimmer, {
|
||||
registerWidgetShim,
|
||||
} from "discourse/widgets/render-glimmer";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
const SEARCH_BUTTON_ID = "search-button";
|
||||
|
||||
let _extraHeaderIcons;
|
||||
clearExtraHeaderIcons();
|
||||
|
||||
let _extraHeaderButtons;
|
||||
clearExtraHeaderButtons();
|
||||
|
||||
export function clearExtraHeaderIcons() {
|
||||
_extraHeaderIcons = headerIconsDAG();
|
||||
}
|
||||
|
||||
export function clearExtraHeaderButtons() {
|
||||
_extraHeaderButtons = headerButtonsDAG();
|
||||
}
|
||||
|
||||
export const dropdown = {
|
||||
buildClasses(attrs) {
|
||||
let classes = attrs.classNames || [];
|
||||
if (attrs.active) {
|
||||
classes.push("active");
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
|
||||
click(e) {
|
||||
if (wantsNewWindow(e)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
if (!this.attrs.active) {
|
||||
this.sendWidgetAction(this.attrs.action);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
createWidget("header-notifications", {
|
||||
services: ["user-tips"],
|
||||
|
||||
settings: {
|
||||
avatarSize: "medium",
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
const { user } = attrs;
|
||||
|
||||
let avatarAttrs = {
|
||||
template: user.get("avatar_template"),
|
||||
username: user.get("username"),
|
||||
};
|
||||
|
||||
if (this.siteSettings.enable_names) {
|
||||
avatarAttrs.name = user.get("name");
|
||||
}
|
||||
|
||||
const contents = [
|
||||
avatarImg(
|
||||
this.settings.avatarSize,
|
||||
Object.assign(
|
||||
{ alt: "user.avatar.header_title" },
|
||||
addExtraUserClasses(user, avatarAttrs)
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
if (this.currentUser && this._shouldHighlightAvatar()) {
|
||||
contents.push(this.attach("header-user-tip-shim"));
|
||||
}
|
||||
|
||||
if (this.currentUser.status) {
|
||||
contents.push(this.attach("user-status-bubble", this.currentUser.status));
|
||||
}
|
||||
|
||||
if (user.isInDoNotDisturb()) {
|
||||
contents.push(
|
||||
h("div.do-not-disturb-background", iconNode("discourse-dnd"))
|
||||
);
|
||||
} else {
|
||||
if (user.new_personal_messages_notifications_count) {
|
||||
contents.push(
|
||||
this.attach("link", {
|
||||
action: attrs.action,
|
||||
className: "badge-notification with-icon new-pms",
|
||||
icon: "envelope",
|
||||
omitSpan: true,
|
||||
title: "notifications.tooltip.new_message_notification",
|
||||
titleOptions: {
|
||||
count: user.new_personal_messages_notifications_count,
|
||||
},
|
||||
attributes: {
|
||||
"aria-label": I18n.t(
|
||||
"notifications.tooltip.new_message_notification",
|
||||
{
|
||||
count: user.new_personal_messages_notifications_count,
|
||||
}
|
||||
),
|
||||
},
|
||||
})
|
||||
);
|
||||
} else if (user.unseen_reviewable_count) {
|
||||
contents.push(
|
||||
this.attach("link", {
|
||||
action: attrs.action,
|
||||
className: "badge-notification with-icon new-reviewables",
|
||||
icon: "flag",
|
||||
omitSpan: true,
|
||||
title: "notifications.tooltip.new_reviewable",
|
||||
titleOptions: { count: user.unseen_reviewable_count },
|
||||
attributes: {
|
||||
"aria-label": I18n.t("notifications.tooltip.new_reviewable", {
|
||||
count: user.unseen_reviewable_count,
|
||||
}),
|
||||
},
|
||||
})
|
||||
);
|
||||
} else if (user.all_unread_notifications_count) {
|
||||
contents.push(
|
||||
this.attach("link", {
|
||||
action: attrs.action,
|
||||
className: "badge-notification unread-notifications",
|
||||
rawLabel: user.all_unread_notifications_count,
|
||||
omitSpan: true,
|
||||
title: "notifications.tooltip.regular",
|
||||
titleOptions: { count: user.all_unread_notifications_count },
|
||||
attributes: {
|
||||
"aria-label": I18n.t("user.notifications"),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
},
|
||||
|
||||
_shouldHighlightAvatar() {
|
||||
const attrs = this.attrs;
|
||||
const { user } = attrs;
|
||||
return (
|
||||
!user.read_first_notification &&
|
||||
!user.enforcedSecondFactor &&
|
||||
!attrs.active
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
createWidget(
|
||||
"user-dropdown",
|
||||
Object.assign(
|
||||
{
|
||||
tagName: "li.header-dropdown-toggle.current-user",
|
||||
|
||||
buildId() {
|
||||
return "current-user";
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
return h(
|
||||
"button.icon.btn.no-text.btn-flat",
|
||||
{
|
||||
attributes: {
|
||||
"aria-haspopup": true,
|
||||
"aria-expanded": attrs.active,
|
||||
"aria-label": I18n.t("user.account_possessive", {
|
||||
name: attrs.user.name || attrs.user.username,
|
||||
}),
|
||||
},
|
||||
},
|
||||
this.attach("header-notifications", attrs)
|
||||
);
|
||||
},
|
||||
},
|
||||
dropdown
|
||||
)
|
||||
);
|
||||
|
||||
createWidget(
|
||||
"header-dropdown",
|
||||
Object.assign(
|
||||
{
|
||||
tagName: "li.header-dropdown-toggle",
|
||||
|
||||
html(attrs) {
|
||||
const title = I18n.t(attrs.title);
|
||||
|
||||
const body = [iconNode(attrs.icon)];
|
||||
if (attrs.contents) {
|
||||
body.push(attrs.contents.call(this));
|
||||
}
|
||||
|
||||
return h(
|
||||
"button.icon.btn.no-text.btn-flat",
|
||||
{
|
||||
attributes: {
|
||||
"aria-expanded": attrs.active,
|
||||
"aria-haspopup": true,
|
||||
title,
|
||||
"aria-label": title,
|
||||
id: attrs.iconId,
|
||||
},
|
||||
},
|
||||
body
|
||||
);
|
||||
},
|
||||
},
|
||||
dropdown
|
||||
)
|
||||
);
|
||||
|
||||
createWidget("header-icons", {
|
||||
services: ["search", "header"],
|
||||
tagName: "ul.icons.d-header-icons",
|
||||
|
||||
init() {
|
||||
registerWidgetShim("extra-icon", "span.wrapper", hbs`<@data.component />`);
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
if (this.siteSettings.login_required && !this.currentUser) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const icons = [];
|
||||
|
||||
const resolvedIcons = _extraHeaderIcons.resolve();
|
||||
|
||||
resolvedIcons.forEach((icon) => {
|
||||
if (icon.key === "search") {
|
||||
if (!this.header.headerButtonsHidden.includes("search")) {
|
||||
icons.push(
|
||||
this.attach("header-dropdown", {
|
||||
title: "search.title",
|
||||
icon: "search",
|
||||
iconId: SEARCH_BUTTON_ID,
|
||||
action: "toggleSearchMenu",
|
||||
active: this.search.visible,
|
||||
href: getURL("/search"),
|
||||
classNames: ["search-dropdown"],
|
||||
})
|
||||
);
|
||||
}
|
||||
} else if (icon.key === "user-menu" && attrs.user) {
|
||||
icons.push(
|
||||
this.attach("user-dropdown", {
|
||||
active: attrs.userVisible,
|
||||
action: "toggleUserMenu",
|
||||
user: attrs.user,
|
||||
})
|
||||
);
|
||||
} else if (
|
||||
icon.key === "hamburger" &&
|
||||
(!attrs.sidebarEnabled || this.site.mobileView)
|
||||
) {
|
||||
icons.push(
|
||||
this.attach("header-dropdown", {
|
||||
title: "hamburger_menu",
|
||||
icon: "bars",
|
||||
iconId: "toggle-hamburger-menu",
|
||||
active: attrs.hamburgerVisible,
|
||||
action: "toggleHamburger",
|
||||
href: "",
|
||||
classNames: ["hamburger-dropdown"],
|
||||
})
|
||||
);
|
||||
} else {
|
||||
icons.push(this.attach("extra-icon", { component: icon.value }));
|
||||
}
|
||||
});
|
||||
|
||||
return icons;
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("header-buttons", {
|
||||
services: ["header"],
|
||||
tagName: "span.auth-buttons",
|
||||
|
||||
html(attrs) {
|
||||
if (this.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const buttons = [];
|
||||
|
||||
if (
|
||||
attrs.canSignUp &&
|
||||
!attrs.topic &&
|
||||
!this.header.headerButtonsHidden.includes("signup")
|
||||
) {
|
||||
buttons.push(
|
||||
this.attach("button", {
|
||||
label: "sign_up",
|
||||
className: "btn-primary btn-small sign-up-button",
|
||||
action: "showCreateAccount",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.header.headerButtonsHidden.includes("login")) {
|
||||
buttons.push(
|
||||
this.attach("button", {
|
||||
label: "log_in",
|
||||
className: "btn-primary btn-small login-button",
|
||||
action: "showLogin",
|
||||
icon: "user",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("header-cloak", {
|
||||
tagName: "div.header-cloak",
|
||||
html() {
|
||||
return "";
|
||||
},
|
||||
click() {},
|
||||
scheduleRerender() {},
|
||||
});
|
||||
|
||||
let additionalPanels = [];
|
||||
export function attachAdditionalPanel(name, toggle, transformAttrs) {
|
||||
additionalPanels.push({ name, toggle, transformAttrs });
|
||||
}
|
||||
|
||||
createWidget("hamburger-dropdown-wrapper", {
|
||||
buildAttributes() {
|
||||
return { "data-click-outside": true };
|
||||
},
|
||||
|
||||
html() {
|
||||
return [
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"div.widget-component-connector",
|
||||
hbs`<Sidebar::HamburgerDropdown />`
|
||||
),
|
||||
];
|
||||
},
|
||||
|
||||
click(event) {
|
||||
if (
|
||||
event.target.closest(".sidebar-section-header-button") ||
|
||||
event.target.closest(".sidebar-section-link")
|
||||
) {
|
||||
this.sendWidgetAction("toggleHamburger");
|
||||
}
|
||||
},
|
||||
|
||||
clickOutside(e) {
|
||||
if (
|
||||
e.target.classList.contains("header-cloak") &&
|
||||
!window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||
) {
|
||||
const panel = document.querySelector(".menu-panel");
|
||||
const headerCloak = document.querySelector(".header-cloak");
|
||||
const finishPosition = isDocumentRTL() ? "340px" : "-340px";
|
||||
panel
|
||||
.animate([{ transform: `translate3d(${finishPosition}, 0, 0)` }], {
|
||||
duration: 200,
|
||||
fill: "forwards",
|
||||
easing: "ease-in",
|
||||
})
|
||||
.finished.then(() => {
|
||||
if (isTesting()) {
|
||||
this.sendWidgetAction("toggleHamburger");
|
||||
} else {
|
||||
discourseLater(() => this.sendWidgetAction("toggleHamburger"));
|
||||
}
|
||||
});
|
||||
headerCloak.animate([{ opacity: 0 }], {
|
||||
duration: 200,
|
||||
fill: "forwards",
|
||||
easing: "ease-in",
|
||||
});
|
||||
} else {
|
||||
this.sendWidgetAction("toggleHamburger");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("revamped-user-menu-wrapper", {
|
||||
buildAttributes() {
|
||||
return { "data-click-outside": true };
|
||||
},
|
||||
|
||||
html() {
|
||||
return [
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"div.widget-component-connector",
|
||||
hbs`<UserMenu::Menu @closeUserMenu={{@data.closeUserMenu}} />`,
|
||||
{
|
||||
closeUserMenu: this.closeUserMenu.bind(this),
|
||||
}
|
||||
),
|
||||
];
|
||||
},
|
||||
|
||||
closeUserMenu() {
|
||||
this.sendWidgetAction("toggleUserMenu");
|
||||
},
|
||||
|
||||
clickOutside(e) {
|
||||
if (
|
||||
e.target.classList.contains("header-cloak") &&
|
||||
!window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||
) {
|
||||
const panel = document.querySelector(".menu-panel");
|
||||
const headerCloak = document.querySelector(".header-cloak");
|
||||
const finishPosition = isDocumentRTL() ? "-340px" : "340px";
|
||||
panel
|
||||
.animate([{ transform: `translate3d(${finishPosition}, 0, 0)` }], {
|
||||
duration: 200,
|
||||
fill: "forwards",
|
||||
easing: "ease-in",
|
||||
})
|
||||
.finished.then(() => {
|
||||
if (isTesting) {
|
||||
this.closeUserMenu();
|
||||
} else {
|
||||
discourseLater(() => this.closeUserMenu());
|
||||
}
|
||||
});
|
||||
headerCloak.animate([{ opacity: 0 }], {
|
||||
duration: 200,
|
||||
fill: "forwards",
|
||||
easing: "ease-in",
|
||||
});
|
||||
} else {
|
||||
this.closeUserMenu();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("search-menu-wrapper", {
|
||||
services: ["search"],
|
||||
buildAttributes() {
|
||||
return { "aria-live": "polite" };
|
||||
},
|
||||
|
||||
buildClasses() {
|
||||
return ["search-menu"];
|
||||
},
|
||||
|
||||
html() {
|
||||
return [
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"div.widget-component-connector",
|
||||
hbs`<SearchMenuPanel @closeSearchMenu={{@data.closeSearchMenu}} />`,
|
||||
{
|
||||
closeSearchMenu: this.closeSearchMenu.bind(this),
|
||||
}
|
||||
),
|
||||
];
|
||||
},
|
||||
|
||||
closeSearchMenu() {
|
||||
this.sendWidgetAction("toggleSearchMenu");
|
||||
document.getElementById(SEARCH_BUTTON_ID)?.focus();
|
||||
},
|
||||
|
||||
clickOutside() {
|
||||
this.closeSearchMenu();
|
||||
},
|
||||
});
|
||||
|
||||
export default createWidget("header", {
|
||||
tagName: "header.d-header",
|
||||
buildKey: () => `header`,
|
||||
services: ["router", "search"],
|
||||
|
||||
init() {
|
||||
registerWidgetShim(
|
||||
"extra-button",
|
||||
"span.wrapper",
|
||||
hbs`<@data.component />`
|
||||
);
|
||||
},
|
||||
|
||||
defaultState() {
|
||||
let states = {
|
||||
searchVisible: false,
|
||||
hamburgerVisible: false,
|
||||
userVisible: false,
|
||||
inTopicContext: false,
|
||||
};
|
||||
|
||||
if (this.site.mobileView) {
|
||||
states.skipSearchContext = true;
|
||||
}
|
||||
|
||||
return states;
|
||||
},
|
||||
|
||||
html(attrs, state) {
|
||||
let inTopicRoute = false;
|
||||
if (this.search.inTopicContext) {
|
||||
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
|
||||
}
|
||||
|
||||
let contents = () => {
|
||||
const headerIcons = this.attach("header-icons", {
|
||||
hamburgerVisible: state.hamburgerVisible,
|
||||
userVisible: state.userVisible,
|
||||
searchVisible: this.search.visible,
|
||||
flagCount: attrs.flagCount,
|
||||
user: this.currentUser,
|
||||
sidebarEnabled: attrs.sidebarEnabled,
|
||||
});
|
||||
|
||||
if (attrs.onlyIcons) {
|
||||
return headerIcons;
|
||||
}
|
||||
|
||||
const buttons = [];
|
||||
const resolvedButtons = _extraHeaderButtons.resolve();
|
||||
resolvedButtons.forEach((button) => {
|
||||
if (button.key === "auth") {
|
||||
buttons.push(this.attach("header-buttons", attrs));
|
||||
}
|
||||
buttons.push(this.attach("extra-button", { component: button.value }));
|
||||
});
|
||||
|
||||
const panels = [];
|
||||
panels.push(h("span.header-buttons", buttons), headerIcons);
|
||||
|
||||
if (this.search.visible) {
|
||||
this.search.inTopicContext = this.search.inTopicContext && inTopicRoute;
|
||||
panels.push(this.attach("search-menu-wrapper"));
|
||||
} else if (state.hamburgerVisible) {
|
||||
panels.push(this.attach("hamburger-dropdown-wrapper", {}));
|
||||
} else if (state.userVisible) {
|
||||
panels.push(this.attach("revamped-user-menu-wrapper", {}));
|
||||
}
|
||||
|
||||
additionalPanels.map((panel) => {
|
||||
if (this.state[panel.toggle]) {
|
||||
panels.push(
|
||||
this.attach(
|
||||
panel.name,
|
||||
panel.transformAttrs.call(this, attrs, state)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.site.mobileView || this.site.narrowDesktopView) {
|
||||
panels.push(this.attach("header-cloak"));
|
||||
}
|
||||
|
||||
return panels;
|
||||
};
|
||||
|
||||
const contentsAttrs = {
|
||||
contents,
|
||||
minimized: !!attrs.topic,
|
||||
};
|
||||
|
||||
return [
|
||||
h(
|
||||
"div.wrap",
|
||||
this.attach("header-contents", { ...attrs, ...contentsAttrs })
|
||||
),
|
||||
new RenderGlimmer(
|
||||
this,
|
||||
"div.widget-component-connector",
|
||||
hbs`
|
||||
<PluginOutlet
|
||||
@name="after-header"
|
||||
@outletArgs={{hash minimized=@data.minimized}}
|
||||
/>
|
||||
`,
|
||||
{ minimized: !!attrs.topic }
|
||||
),
|
||||
];
|
||||
},
|
||||
|
||||
updateHighlight() {
|
||||
if (!this.search.visible) {
|
||||
this.search.highlightTerm = "";
|
||||
}
|
||||
},
|
||||
|
||||
closeAll() {
|
||||
this.state.userVisible = false;
|
||||
this.state.hamburgerVisible = false;
|
||||
this.search.visible = false;
|
||||
this.toggleBodyScrolling(false);
|
||||
},
|
||||
|
||||
toggleSearchMenu() {
|
||||
if (this.site.mobileView) {
|
||||
const context = this.search.searchContext;
|
||||
let params = "";
|
||||
|
||||
if (context) {
|
||||
params = `?context=${context.type}&context_id=${context.id}&skip_context=${this.state.skipSearchContext}`;
|
||||
}
|
||||
|
||||
if (this.router.currentRouteName === "full-page-search") {
|
||||
scrollTop();
|
||||
$(".full-page-search").focus();
|
||||
return false;
|
||||
} else {
|
||||
return DiscourseURL.routeTo("/search" + params);
|
||||
}
|
||||
}
|
||||
|
||||
this.search.visible = !this.search.visible;
|
||||
this.updateHighlight();
|
||||
|
||||
if (!this.search.searchVisible) {
|
||||
this.search.inTopicContext = false;
|
||||
}
|
||||
},
|
||||
|
||||
toggleUserMenu() {
|
||||
this.state.userVisible = !this.state.userVisible;
|
||||
this.toggleBodyScrolling(this.state.userVisible);
|
||||
|
||||
// auto focus on first button in dropdown
|
||||
schedule("afterRender", () =>
|
||||
document.querySelector(".user-menu button")?.focus()
|
||||
);
|
||||
},
|
||||
|
||||
toggleHamburger() {
|
||||
if (this.attrs.sidebarEnabled && !this.site.narrowDesktopView) {
|
||||
if (!this.attrs.showSidebar) {
|
||||
this.sendWidgetAction("toggleSidebar");
|
||||
this.closeAll();
|
||||
} else {
|
||||
this.state.hamburgerVisible = !this.state.hamburgerVisible;
|
||||
}
|
||||
} else {
|
||||
this.state.hamburgerVisible = !this.state.hamburgerVisible;
|
||||
this.toggleBodyScrolling(this.state.hamburgerVisible);
|
||||
schedule("afterRender", () => {
|
||||
// Remove focus from hamburger toggle button
|
||||
document.querySelector("#toggle-hamburger-menu")?.blur();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
toggleBodyScrolling(bool) {
|
||||
if (this.site.mobileView) {
|
||||
scrollLock(bool);
|
||||
}
|
||||
},
|
||||
|
||||
togglePageSearch() {
|
||||
this.search.inTopicContext = false;
|
||||
let showSearch = this.router.currentRouteName.startsWith("topic.");
|
||||
|
||||
// If we're viewing a topic, only intercept search if there are cloaked posts
|
||||
if (showSearch) {
|
||||
const controller = this.register.lookup("controller:topic");
|
||||
const total = controller.get("model.postStream.stream.length") || 0;
|
||||
const chunkSize = controller.get("model.chunk_size") || 0;
|
||||
|
||||
showSearch =
|
||||
total > chunkSize &&
|
||||
$(".topic-post .cooked, .small-action:not(.time-gap)").length < total;
|
||||
}
|
||||
|
||||
if (this.search.visible) {
|
||||
this.toggleSearchMenu();
|
||||
return showSearch;
|
||||
}
|
||||
|
||||
if (showSearch) {
|
||||
this.search.inTopicContext = true;
|
||||
this.toggleSearchMenu();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
domClean() {
|
||||
const { state } = this;
|
||||
|
||||
if (this.search.visible || state.hamburgerVisible || state.userVisible) {
|
||||
this.closeAll();
|
||||
}
|
||||
},
|
||||
|
||||
headerKeyboardTrigger(msg) {
|
||||
switch (msg.type) {
|
||||
case "search":
|
||||
this.toggleSearchMenu();
|
||||
break;
|
||||
case "user":
|
||||
this.toggleUserMenu();
|
||||
break;
|
||||
case "hamburger":
|
||||
this.toggleHamburger();
|
||||
break;
|
||||
case "page-search":
|
||||
if (!this.togglePageSearch()) {
|
||||
msg.event.preventDefault();
|
||||
msg.event.stopPropagation();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
||||
|
||||
registerWidgetShim(
|
||||
"home-logo-wrapper-outlet",
|
||||
"div.home-logo-wrapper-outlet",
|
||||
hbs`<PluginOutlet @name="home-logo-wrapper">
|
||||
<PluginOutlet @name="home-logo" @outletArgs={{hash minimized=@data.topic}}>
|
||||
<MountWidget @widget="home-logo" @args={{@data}} />
|
||||
</PluginOutlet>
|
||||
</PluginOutlet>`
|
||||
);
|
|
@ -1,152 +0,0 @@
|
|||
// deprecated in favor of components/header/home-logo.gjs
|
||||
import { h } from "virtual-dom";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import { applyValueTransformer } from "discourse/lib/transformer";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import Session from "discourse/models/session";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default createWidget("home-logo", {
|
||||
services: ["session"],
|
||||
tagName: "div.title",
|
||||
|
||||
settings: {
|
||||
href: getURL("/"),
|
||||
},
|
||||
|
||||
buildClasses() {
|
||||
if (this.attrs.minimized) {
|
||||
return "title--minimized";
|
||||
}
|
||||
},
|
||||
|
||||
href() {
|
||||
const href = this.settings.href;
|
||||
|
||||
return applyValueTransformer(
|
||||
"home-logo-href",
|
||||
typeof href === "function" ? href() : href
|
||||
);
|
||||
},
|
||||
|
||||
logoUrl(opts = {}) {
|
||||
return this.logoResolver("logo", opts);
|
||||
},
|
||||
|
||||
mobileLogoUrl(opts = {}) {
|
||||
return this.logoResolver("mobile_logo", opts);
|
||||
},
|
||||
|
||||
smallLogoUrl(opts = {}) {
|
||||
return this.logoResolver("logo_small", opts);
|
||||
},
|
||||
|
||||
logo() {
|
||||
const darkModeOptions = this.session.darkModeAvailable
|
||||
? { dark: true }
|
||||
: {};
|
||||
|
||||
const mobileLogoUrl = this.mobileLogoUrl(),
|
||||
mobileLogoUrlDark = this.mobileLogoUrl(darkModeOptions);
|
||||
|
||||
const showMobileLogo = this.site.mobileView && mobileLogoUrl.length > 0;
|
||||
|
||||
const logoUrl = this.logoUrl(),
|
||||
logoUrlDark = this.logoUrl(darkModeOptions);
|
||||
const title = this.siteSettings.title;
|
||||
|
||||
if (this.attrs.minimized) {
|
||||
const logoSmallUrl = this.smallLogoUrl(),
|
||||
logoSmallUrlDark = this.smallLogoUrl(darkModeOptions);
|
||||
if (logoSmallUrl.length) {
|
||||
return this.logoElement(
|
||||
"logo-small",
|
||||
logoSmallUrl,
|
||||
title,
|
||||
logoSmallUrlDark
|
||||
);
|
||||
} else {
|
||||
return iconNode("home");
|
||||
}
|
||||
} else if (showMobileLogo) {
|
||||
return this.logoElement(
|
||||
"logo-mobile",
|
||||
mobileLogoUrl,
|
||||
title,
|
||||
mobileLogoUrlDark
|
||||
);
|
||||
} else if (logoUrl.length) {
|
||||
return this.logoElement("logo-big", logoUrl, title, logoUrlDark);
|
||||
} else {
|
||||
return h("h1#site-text-logo.text-logo", { key: "logo-text" }, title);
|
||||
}
|
||||
},
|
||||
|
||||
logoResolver(name, opts = {}) {
|
||||
const { siteSettings } = this;
|
||||
|
||||
// get alternative logos for browser dark dark mode switching
|
||||
if (opts.dark) {
|
||||
return siteSettings[`site_${name}_dark_url`];
|
||||
}
|
||||
|
||||
// try dark logos first when color scheme is dark
|
||||
// this is independent of browser dark mode
|
||||
// hence the fallback to normal logos
|
||||
if (Session.currentProp("defaultColorSchemeIsDark")) {
|
||||
return (
|
||||
siteSettings[`site_${name}_dark_url`] ||
|
||||
siteSettings[`site_${name}_url`] ||
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
return siteSettings[`site_${name}_url`] || "";
|
||||
},
|
||||
|
||||
logoElement(key, url, title, darkUrl = null) {
|
||||
const attributes =
|
||||
key === "logo-small"
|
||||
? { src: getURL(url), width: 36, alt: title }
|
||||
: { src: getURL(url), alt: title };
|
||||
|
||||
const imgElement = h(`img#site-logo.${key}`, {
|
||||
key,
|
||||
attributes,
|
||||
});
|
||||
|
||||
if (darkUrl && url !== darkUrl) {
|
||||
return h("picture", [
|
||||
h("source", {
|
||||
attributes: {
|
||||
srcset: getURL(darkUrl),
|
||||
media: "(prefers-color-scheme: dark)",
|
||||
},
|
||||
}),
|
||||
imgElement,
|
||||
]);
|
||||
}
|
||||
|
||||
return imgElement;
|
||||
},
|
||||
|
||||
html() {
|
||||
return h(
|
||||
"a",
|
||||
{ attributes: { href: this.href(), "data-auto-route": true } },
|
||||
this.logo()
|
||||
);
|
||||
},
|
||||
|
||||
click(e) {
|
||||
if (wantsNewWindow(e)) {
|
||||
return false;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
DiscourseURL.routeToTag(e.target.closest("a"));
|
||||
return false;
|
||||
},
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
// Deprecated in favor of app/assets/javascripts/discourse/app/components/header/sidebar-toggle.gjs
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
|
||||
export default createWidget("sidebar-toggle", {
|
||||
tagName: "span.header-sidebar-toggle",
|
||||
|
||||
html() {
|
||||
const attrs = this.attrs;
|
||||
|
||||
return [
|
||||
this.attach("button", {
|
||||
title: "sidebar.title",
|
||||
icon: "bars",
|
||||
action: this.site.narrowDesktopView
|
||||
? "toggleHamburger"
|
||||
: "toggleSidebar",
|
||||
className: `btn btn-flat btn-sidebar-toggle ${
|
||||
this.site.narrowDesktopView ? "narrow-desktop" : ""
|
||||
}`,
|
||||
ariaExpanded: attrs.showSidebar ? "true" : "false",
|
||||
ariaControls: "d-sidebar",
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
// deprecated in favor of app/components/header/user-dropdown/user-status-bubble.gjs
|
||||
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default createWidget("user-status-bubble", {
|
||||
tagName: "div.user-status-background",
|
||||
|
||||
html(attrs) {
|
||||
let title = attrs.description;
|
||||
if (attrs.ends_at) {
|
||||
const until = moment
|
||||
.tz(attrs.ends_at, this.currentUser.user_option.timezone)
|
||||
.format(I18n.t("dates.long_date_without_year"));
|
||||
title += `\n${I18n.t("until")} ${until}`;
|
||||
}
|
||||
|
||||
return this.attach("emoji", { name: attrs.emoji, title });
|
||||
},
|
||||
});
|
|
@ -6,34 +6,6 @@ import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
|||
|
||||
// TODO: Consolidate these tests into a single acceptance test once the Glimmer
|
||||
// header is the default.
|
||||
acceptance("Header API - authenticated", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
glimmer_header_mode: "disabled",
|
||||
});
|
||||
|
||||
test("can add buttons to the header", async function (assert) {
|
||||
withPluginApi("1.29.0", (api) => {
|
||||
api.headerButtons.add("test", <template>
|
||||
<button class="test-button">Test</button>
|
||||
</template>);
|
||||
});
|
||||
|
||||
await visit("/");
|
||||
assert.dom("button.test-button").exists("button is displayed");
|
||||
});
|
||||
|
||||
test("can add icons to the header", async function (assert) {
|
||||
withPluginApi("1.29.0", (api) => {
|
||||
api.headerIcons.add("test", <template>
|
||||
<span class="test-icon">Test</span>
|
||||
</template>);
|
||||
});
|
||||
|
||||
await visit("/");
|
||||
assert.dom(".test-icon").exists("icon is displayed");
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Header API - anonymous", function () {
|
||||
test("can add buttons to the header", async function (assert) {
|
||||
|
@ -95,9 +67,6 @@ acceptance("Header API - anonymous", function () {
|
|||
|
||||
acceptance("Glimmer Header API - authenticated", function (needs) {
|
||||
needs.user({ groups: AUTO_GROUPS.everyone });
|
||||
needs.settings({
|
||||
glimmer_header_mode: "enabled",
|
||||
});
|
||||
|
||||
test("can add buttons to the header", async function (assert) {
|
||||
withPluginApi("1.29.0", (api) => {
|
||||
|
|
|
@ -65,9 +65,6 @@ async function triggerSwipeEnd({ x, y, touchTarget }) {
|
|||
acceptance("Mobile - menu swipes", function (needs) {
|
||||
needs.mobileView();
|
||||
needs.user();
|
||||
needs.settings({
|
||||
glimmer_header_mode: "enabled",
|
||||
});
|
||||
|
||||
chromeTest("swipe to close hamburger", async function (assert) {
|
||||
await visit("/");
|
||||
|
|
|
@ -89,10 +89,6 @@ import {
|
|||
currentSettings,
|
||||
mergeSettings,
|
||||
} from "discourse/tests/helpers/site-settings";
|
||||
import {
|
||||
clearExtraHeaderButtons,
|
||||
clearExtraHeaderIcons,
|
||||
} from "discourse/widgets/header";
|
||||
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
||||
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
|
||||
import { resetDecorators } from "discourse/widgets/widget";
|
||||
|
@ -236,8 +232,6 @@ export function testCleanup(container, app) {
|
|||
resetSidebarPanels();
|
||||
clearExtraGlimmerHeaderIcons();
|
||||
clearExtraGlimmerHeaderButtons();
|
||||
clearExtraHeaderIcons();
|
||||
clearExtraHeaderButtons();
|
||||
resetOnKeyUpCallbacks();
|
||||
resetLogSearchLinkClickedCallbacks();
|
||||
resetItemSelectCallbacks();
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
// deprecated in favor of spec/system/header/site_header_spec.rb
|
||||
|
||||
import {
|
||||
click,
|
||||
render,
|
||||
settled,
|
||||
triggerKeyEvent,
|
||||
waitUntil,
|
||||
} from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
module("Integration | Component | site-header", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.currentUser.set("unread_high_priority_notifications", 1);
|
||||
this.currentUser.set("read_first_notification", false);
|
||||
});
|
||||
|
||||
test("unread notifications count rerenders when user's notifications count is updated", async function (assert) {
|
||||
this.currentUser.set("all_unread_notifications_count", 1);
|
||||
|
||||
await render(hbs`<SiteHeader />`);
|
||||
let unreadBadge = query(
|
||||
".header-dropdown-toggle.current-user .unread-notifications"
|
||||
);
|
||||
assert.strictEqual(unreadBadge.textContent, "1");
|
||||
|
||||
this.currentUser.set("all_unread_notifications_count", 5);
|
||||
await settled();
|
||||
|
||||
unreadBadge = query(
|
||||
".header-dropdown-toggle.current-user .unread-notifications"
|
||||
);
|
||||
assert.strictEqual(unreadBadge.textContent, "5");
|
||||
});
|
||||
|
||||
test("hamburger menu icon doesn't show pending reviewables count for non-legacy navigation menu", async function (assert) {
|
||||
this.currentUser.set("reviewable_count", 1);
|
||||
this.siteSettings.navigation_menu = "sidebar";
|
||||
await render(hbs`<SiteHeader />`);
|
||||
assert.ok(!exists(".hamburger-dropdown .badge-notification"));
|
||||
});
|
||||
|
||||
test("clicking outside the revamped menu closes it", async function (assert) {
|
||||
await render(hbs`<SiteHeader />`);
|
||||
await click(".header-dropdown-toggle.current-user");
|
||||
assert.ok(exists(".user-menu.revamped"));
|
||||
await click("header.d-header");
|
||||
assert.ok(!exists(".user-menu.revamped"));
|
||||
});
|
||||
|
||||
test("header's height is setting css property", async function (assert) {
|
||||
await render(hbs`<SiteHeader />`);
|
||||
|
||||
function getProperty() {
|
||||
const rawValue = getComputedStyle(document.body).getPropertyValue(
|
||||
"--header-offset"
|
||||
);
|
||||
const roundedValue = Math.floor(parseFloat(rawValue));
|
||||
return roundedValue + "px";
|
||||
}
|
||||
|
||||
document.querySelector(".d-header").style.height = 90 + "px";
|
||||
await waitUntil(() => getProperty() === "90px", { timeout: 100 });
|
||||
assert.strictEqual(getProperty(), "90px");
|
||||
|
||||
document.querySelector(".d-header").style.height = 60 + "px";
|
||||
await waitUntil(() => getProperty() === "60px", { timeout: 100 });
|
||||
assert.strictEqual(getProperty(), "60px");
|
||||
});
|
||||
|
||||
test("arrow up/down keys move focus between the tabs", async function (assert) {
|
||||
this.currentUser.set("can_send_private_messages", true);
|
||||
await render(hbs`<SiteHeader />`);
|
||||
await click(".header-dropdown-toggle.current-user");
|
||||
let activeTab = query(".menu-tabs-container .btn.active");
|
||||
assert.strictEqual(activeTab.id, "user-menu-button-all-notifications");
|
||||
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
let focusedTab = document.activeElement;
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-replies",
|
||||
"pressing the down arrow key moves focus to the next tab towards the bottom"
|
||||
);
|
||||
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
|
||||
focusedTab = document.activeElement;
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-profile",
|
||||
"the down arrow key can move the focus to the bottom tabs"
|
||||
);
|
||||
|
||||
await triggerKeyEvent(document, "keydown", "ArrowDown");
|
||||
focusedTab = document.activeElement;
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-all-notifications",
|
||||
"the focus moves back to the top after reaching the bottom"
|
||||
);
|
||||
|
||||
await triggerKeyEvent(document, "keydown", "ArrowUp");
|
||||
focusedTab = document.activeElement;
|
||||
assert.strictEqual(
|
||||
focusedTab.id,
|
||||
"user-menu-button-profile",
|
||||
"the up arrow key moves the focus in the opposite direction"
|
||||
);
|
||||
});
|
||||
|
||||
test("new personal messages bubble is prioritized over unseen reviewables and regular notifications bubbles", async function (assert) {
|
||||
this.currentUser.set("all_unread_notifications_count", 5);
|
||||
this.currentUser.set("new_personal_messages_notifications_count", 2);
|
||||
this.currentUser.set("unseen_reviewable_count", 3);
|
||||
|
||||
await render(hbs`<SiteHeader />`);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
|
||||
),
|
||||
"regular notifications bubble isn't displayed when there are new personal messages notifications"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
|
||||
),
|
||||
"reviewables bubble isn't displayed when there are new personal messages notifications"
|
||||
);
|
||||
|
||||
const pmsBubble = query(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
|
||||
);
|
||||
assert.strictEqual(
|
||||
pmsBubble.textContent.trim(),
|
||||
"",
|
||||
"personal messages bubble has no count"
|
||||
);
|
||||
assert.ok(
|
||||
pmsBubble.querySelector(".d-icon-envelope"),
|
||||
"personal messages bubble has envelope icon"
|
||||
);
|
||||
assert.strictEqual(
|
||||
pmsBubble.title,
|
||||
I18n.t("notifications.tooltip.new_message_notification", { count: 2 }),
|
||||
"personal messages bubble bubble has a title"
|
||||
);
|
||||
});
|
||||
|
||||
test("unseen reviewables bubble is prioritized over regular notifications", async function (assert) {
|
||||
this.currentUser.set("all_unread_notifications_count", 5);
|
||||
this.currentUser.set("new_personal_messages_notifications_count", 0);
|
||||
this.currentUser.set("unseen_reviewable_count", 3);
|
||||
await render(hbs`<SiteHeader />`);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
|
||||
),
|
||||
"regular notifications bubble isn't displayed when there are unseen reviewables notifications"
|
||||
);
|
||||
|
||||
const reviewablesBubble = query(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
|
||||
);
|
||||
assert.strictEqual(
|
||||
reviewablesBubble.textContent.trim(),
|
||||
"",
|
||||
"reviewables bubble has no count"
|
||||
);
|
||||
assert.ok(
|
||||
reviewablesBubble.querySelector(".d-icon-flag"),
|
||||
"reviewables bubble has flag icon"
|
||||
);
|
||||
assert.strictEqual(
|
||||
reviewablesBubble.title,
|
||||
I18n.t("notifications.tooltip.new_reviewable", { count: 3 }),
|
||||
"reviewables bubble has a title"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
|
||||
),
|
||||
"personal messages bubble isn't displayed"
|
||||
);
|
||||
});
|
||||
|
||||
test("regular notifications bubble is shown if there are neither new personal messages nor unseen reviewables", async function (assert) {
|
||||
this.currentUser.set("all_unread_notifications_count", 5);
|
||||
this.currentUser.set("new_personal_messages_notifications_count", 0);
|
||||
this.currentUser.set("unseen_reviewable_count", 0);
|
||||
await render(hbs`<SiteHeader />`);
|
||||
|
||||
const regularNotificationsBubble = query(
|
||||
".header-dropdown-toggle.current-user .badge-notification.unread-notifications"
|
||||
);
|
||||
assert.strictEqual(
|
||||
regularNotificationsBubble.textContent,
|
||||
"5",
|
||||
"regular notifications bubble has a count"
|
||||
);
|
||||
assert.strictEqual(
|
||||
regularNotificationsBubble.title,
|
||||
I18n.t("notifications.tooltip.regular", { count: 5 }),
|
||||
"regular notifications bubble has a title"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables"
|
||||
),
|
||||
"reviewables bubble isn't displayed"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms"
|
||||
),
|
||||
"personal messages bubble isn't displayed"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,87 +0,0 @@
|
|||
// deprecated in favor of spec/system/header/header_spec.rb
|
||||
|
||||
import { click, render } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
module("Integration | Component | Widget | header", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("rendering basics", async function (assert) {
|
||||
await render(hbs`<MountWidget @widget="header" />`);
|
||||
|
||||
assert.ok(exists("header.d-header"));
|
||||
assert.ok(exists("#site-logo"));
|
||||
});
|
||||
|
||||
test("sign up / login buttons", async function (assert) {
|
||||
this.owner.unregister("service:current-user");
|
||||
this.set("args", { canSignUp: true });
|
||||
this.set("showCreateAccount", () => (this.signupShown = true));
|
||||
this.set("showLogin", () => (this.loginShown = true));
|
||||
|
||||
await render(hbs`
|
||||
<MountWidget
|
||||
@widget="header"
|
||||
@showCreateAccount={{this.showCreateAccount}}
|
||||
@showLogin={{this.showLogin}}
|
||||
@args={{this.args}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.ok(exists("button.sign-up-button"));
|
||||
assert.ok(exists("button.login-button"));
|
||||
|
||||
await click("button.sign-up-button");
|
||||
assert.ok(this.signupShown);
|
||||
|
||||
await click("button.login-button");
|
||||
assert.ok(this.loginShown);
|
||||
});
|
||||
|
||||
test("anon when login required", async function (assert) {
|
||||
this.owner.unregister("service:current-user");
|
||||
this.set("args", { canSignUp: true });
|
||||
this.set("showCreateAccount", () => (this.signupShown = true));
|
||||
this.set("showLogin", () => (this.loginShown = true));
|
||||
this.siteSettings.login_required = true;
|
||||
|
||||
await render(hbs`
|
||||
<MountWidget
|
||||
@widget="header"
|
||||
@showCreateAccount={{this.showCreateAccount}}
|
||||
@showLogin={{this.showLogin}}
|
||||
@args={{this.args}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.ok(exists("button.login-button"));
|
||||
assert.ok(exists("button.sign-up-button"));
|
||||
assert.ok(!exists("#search-button"));
|
||||
assert.ok(!exists("#toggle-hamburger-menu"));
|
||||
});
|
||||
|
||||
test("logged in when login required", async function (assert) {
|
||||
this.set("args", { canSignUp: true });
|
||||
this.set("showCreateAccount", () => (this.signupShown = true));
|
||||
this.set("showLogin", () => (this.loginShown = true));
|
||||
this.siteSettings.login_required = true;
|
||||
|
||||
await render(hbs`
|
||||
<MountWidget
|
||||
@widget="header"
|
||||
@showCreateAccount={{this.showCreateAccount}}
|
||||
@showLogin={{this.showLogin}}
|
||||
@args={{this.args}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.ok(!exists("button.login-button"));
|
||||
assert.ok(!exists("button.sign-up-button"));
|
||||
assert.ok(exists("#search-button"));
|
||||
assert.ok(exists("#toggle-hamburger-menu"));
|
||||
assert.ok(exists("#current-user"));
|
||||
});
|
||||
});
|
|
@ -1,231 +0,0 @@
|
|||
// deprecated in favor of discourse/tests/integration/components/home-logo-test.gjs
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import MountWidget from "discourse/components/mount-widget";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { count, exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
const bigLogo = "/images/d-logo-sketch.png?test";
|
||||
const smallLogo = "/images/d-logo-sketch-small.png?test";
|
||||
const mobileLogo = "/images/d-logo-sketch.png?mobile";
|
||||
const darkLogo = "/images/d-logo-sketch.png?dark";
|
||||
const title = "Cool Forum";
|
||||
const prefersDark = "(prefers-color-scheme: dark)";
|
||||
|
||||
module("Integration | Component | Widget | home-logo", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.afterEach(function () {
|
||||
this.session = getOwner(this).lookup("service:session");
|
||||
this.session.set("darkModeAvailable", null);
|
||||
this.session.set("defaultColorSchemeIsDark", null);
|
||||
});
|
||||
|
||||
test("basics", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_small_url = smallLogo;
|
||||
this.siteSettings.title = title;
|
||||
const args = { minimized: false };
|
||||
|
||||
await render(<template>
|
||||
<MountWidget @widget="home-logo" @args={{args}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(count(".title"), 1);
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("alt"), title);
|
||||
});
|
||||
|
||||
test("basics - minimized", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_small_url = smallLogo;
|
||||
this.siteSettings.title = title;
|
||||
const args = { minimized: true };
|
||||
|
||||
await render(<template>
|
||||
<MountWidget @widget="home-logo" @args={{args}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(count("img.logo-small"), 1);
|
||||
assert.strictEqual(query("img.logo-small").getAttribute("src"), smallLogo);
|
||||
assert.strictEqual(query("img.logo-small").getAttribute("alt"), title);
|
||||
assert.strictEqual(query("img.logo-small").getAttribute("width"), "36");
|
||||
});
|
||||
|
||||
test("no logo", async function (assert) {
|
||||
this.siteSettings.site_logo_url = "";
|
||||
this.siteSettings.site_logo_small_url = "";
|
||||
this.siteSettings.title = title;
|
||||
const args = { minimized: false };
|
||||
|
||||
await render(<template>
|
||||
<MountWidget @widget="home-logo" @args={{args}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(count("h1#site-text-logo.text-logo"), 1);
|
||||
assert.strictEqual(query("#site-text-logo").innerText, title);
|
||||
});
|
||||
|
||||
test("no logo - minimized", async function (assert) {
|
||||
this.siteSettings.site_logo_url = "";
|
||||
this.siteSettings.site_logo_small_url = "";
|
||||
this.siteSettings.title = title;
|
||||
const args = { minimized: true };
|
||||
|
||||
await render(<template>
|
||||
<MountWidget @widget="home-logo" @args={{args}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(count(".d-icon-home"), 1);
|
||||
});
|
||||
|
||||
test("mobile logo", async function (assert) {
|
||||
this.siteSettings.site_mobile_logo_url = mobileLogo;
|
||||
this.siteSettings.site_logo_small_url = smallLogo;
|
||||
this.site.mobileView = true;
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-mobile"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), mobileLogo);
|
||||
});
|
||||
|
||||
test("mobile without logo", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.site.mobileView = true;
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
|
||||
});
|
||||
|
||||
test("logo with dark mode alternative", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
this.session.set("darkModeAvailable", true);
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
|
||||
|
||||
assert.strictEqual(
|
||||
query("picture source").getAttribute("media"),
|
||||
prefersDark,
|
||||
"includes dark mode media attribute"
|
||||
);
|
||||
assert.strictEqual(
|
||||
query("picture source").getAttribute("srcset"),
|
||||
darkLogo,
|
||||
"includes dark mode alternative logo source"
|
||||
);
|
||||
});
|
||||
|
||||
test("mobile logo with dark mode alternative", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_mobile_logo_url = mobileLogo;
|
||||
this.siteSettings.site_mobile_logo_dark_url = darkLogo;
|
||||
this.session.set("darkModeAvailable", true);
|
||||
|
||||
this.site.mobileView = true;
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), mobileLogo);
|
||||
|
||||
assert.strictEqual(
|
||||
query("picture source").getAttribute("media"),
|
||||
prefersDark,
|
||||
"includes dark mode media attribute"
|
||||
);
|
||||
assert.strictEqual(
|
||||
query("picture source").getAttribute("srcset"),
|
||||
darkLogo,
|
||||
"includes dark mode alternative logo source"
|
||||
);
|
||||
});
|
||||
|
||||
test("dark mode enabled but no dark logo set", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = "";
|
||||
this.session.set("darkModeAvailable", true);
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
|
||||
assert.ok(!exists("picture"), "does not include alternative logo");
|
||||
});
|
||||
|
||||
test("dark logo set but no dark mode", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(query("#site-logo").getAttribute("src"), bigLogo);
|
||||
assert.ok(!exists("picture"), "does not include alternative logo");
|
||||
});
|
||||
|
||||
test("dark color scheme and dark logo set", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = darkLogo;
|
||||
this.session.set("defaultColorSchemeIsDark", true);
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(
|
||||
query("#site-logo").getAttribute("src"),
|
||||
darkLogo,
|
||||
"uses dark logo"
|
||||
);
|
||||
assert.ok(!exists("picture"), "does not add dark mode alternative");
|
||||
});
|
||||
|
||||
test("dark color scheme and dark logo not set", async function (assert) {
|
||||
this.siteSettings.site_logo_url = bigLogo;
|
||||
this.siteSettings.site_logo_dark_url = "";
|
||||
this.session.set("defaultColorSchemeIsDark", true);
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
assert.strictEqual(count("img#site-logo.logo-big"), 1);
|
||||
assert.strictEqual(
|
||||
query("#site-logo").getAttribute("src"),
|
||||
bigLogo,
|
||||
"uses regular logo on dark scheme if no dark logo"
|
||||
);
|
||||
});
|
||||
|
||||
test("the home logo href url defaults to /", async function (assert) {
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
const anchorElement = query("#site-logo").closest("a");
|
||||
assert.strictEqual(
|
||||
anchorElement.getAttribute("href"),
|
||||
"/",
|
||||
"home logo href equals /"
|
||||
);
|
||||
});
|
||||
|
||||
test("api.registerHomeLogoHrefCallback can be used to change the logo href url", async function (assert) {
|
||||
withPluginApi("1.32.0", (api) => {
|
||||
api.registerHomeLogoHrefCallback(() => "https://example.com");
|
||||
});
|
||||
|
||||
await render(<template><MountWidget @widget="home-logo" /></template>);
|
||||
|
||||
const anchorElement = query("#site-logo").closest("a");
|
||||
assert.strictEqual(
|
||||
anchorElement.getAttribute("href"),
|
||||
"https://example.com",
|
||||
"home logo href equals the one set by the callback"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -2692,7 +2692,6 @@ en:
|
|||
enable_experimental_lightbox: "EXPERIMENTAL: Replace the default image lightbox with the revamped design."
|
||||
enable_experimental_bookmark_redesign_groups: "EXPERIMENTAL: Show a quick access menu for bookmarks on posts and a new redesigned modal"
|
||||
experimental_redesigned_about_page_groups: "EXPERIMENTAL: Enable the redesigned /about page for specific groups."
|
||||
glimmer_header_mode: "Control whether the new 'glimmer' header implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. https://meta.discourse.org/t/316549"
|
||||
experimental_glimmer_topic_list_groups: "EXPERIMENTAL: Enable the new 'glimmer' topic list implementation. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
|
||||
experimental_form_templates: "EXPERIMENTAL: Enable the form templates feature. <b>After enabled,</b> manage the templates at <a href='%{base_path}/admin/customize/form-templates'>Customize / Templates</a>."
|
||||
admin_sidebar_enabled_groups: "Enable sidebar navigation for the admin UI for the specified groups, which replaces the top-level admin navigation buttons."
|
||||
|
|
|
@ -2432,14 +2432,6 @@ developer:
|
|||
instrument_gc_stat_per_request:
|
||||
default: false
|
||||
hidden: true
|
||||
glimmer_header_mode:
|
||||
client: true
|
||||
type: enum
|
||||
choices:
|
||||
- disabled
|
||||
- auto
|
||||
- enabled
|
||||
default: enabled
|
||||
admin_sidebar_enabled_groups:
|
||||
type: group_list
|
||||
list_type: compact
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
#
|
||||
class RemoveGlimmerHeaderModeSetting < ActiveRecord::Migration[7.1]
|
||||
def up
|
||||
execute <<~SQL
|
||||
DELETE FROM site_settings
|
||||
WHERE name = 'glimmer_header_mode'
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ RSpec.describe "Glimmer Header", type: :system do
|
|||
let(:search) { PageObjects::Pages::Search.new }
|
||||
fab!(:current_user) { Fabricate(:user) }
|
||||
fab!(:topic)
|
||||
before { SiteSetting.glimmer_header_mode = "enabled" }
|
||||
|
||||
it "renders basics" do
|
||||
visit "/"
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Legacy Widget Header", type: :system do
|
||||
before { SiteSetting.glimmer_header_mode = "disabled" }
|
||||
|
||||
context "when resetting password" do
|
||||
fab!(:current_user) { Fabricate(:user) }
|
||||
|
||||
it "does not show search, login, or signup buttons" do
|
||||
email_token =
|
||||
current_user.email_tokens.create!(
|
||||
email: current_user.email,
|
||||
scope: EmailToken.scopes[:password_reset],
|
||||
)
|
||||
|
||||
visit "/u/password-reset/#{email_token.token}"
|
||||
expect(page).not_to have_css("button.login-button")
|
||||
expect(page).not_to have_css("button.sign-up-button")
|
||||
expect(page).not_to have_css(".search-dropdown #search-button")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user