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 { addGlobalNotice } from "discourse/components/global-notice";
|
||||||
import { headerButtonsDAG } from "discourse/components/header";
|
import { headerButtonsDAG } from "discourse/components/header";
|
||||||
import { headerIconsDAG } from "discourse/components/header/icons";
|
import { headerIconsDAG } from "discourse/components/header/icons";
|
||||||
import MountWidget, {
|
import { addWidgetCleanCallback } from "discourse/components/mount-widget";
|
||||||
addWidgetCleanCallback,
|
|
||||||
} from "discourse/components/mount-widget";
|
|
||||||
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
|
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
|
||||||
import {
|
import {
|
||||||
addPluginReviewableParam,
|
addPluginReviewableParam,
|
||||||
|
@ -40,7 +38,6 @@ import {
|
||||||
} from "discourse/components/search-menu/results/random-quick-tip";
|
} from "discourse/components/search-menu/results/random-quick-tip";
|
||||||
import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term";
|
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 { 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 { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant";
|
||||||
import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container";
|
import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container";
|
||||||
import { addTopicTitleDecorator } from "discourse/components/topic-title";
|
import { addTopicTitleDecorator } from "discourse/components/topic-title";
|
||||||
|
@ -127,7 +124,6 @@ import {
|
||||||
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
||||||
import { setNotificationsLimit } from "discourse/routes/user-notifications";
|
import { setNotificationsLimit } from "discourse/routes/user-notifications";
|
||||||
import { addComposerSaveErrorCallback } from "discourse/services/composer";
|
import { addComposerSaveErrorCallback } from "discourse/services/composer";
|
||||||
import { attachAdditionalPanel } from "discourse/widgets/header";
|
|
||||||
import { addPostClassesCallback } from "discourse/widgets/post";
|
import { addPostClassesCallback } from "discourse/widgets/post";
|
||||||
import { addDecorator } from "discourse/widgets/post-cooked";
|
import { addDecorator } from "discourse/widgets/post-cooked";
|
||||||
import {
|
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 { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
|
||||||
import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
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();
|
const appliedModificationIds = new WeakMap();
|
||||||
|
|
||||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||||
|
@ -732,7 +714,7 @@ class PluginApi {
|
||||||
**/
|
**/
|
||||||
decorateWidget(name, fn) {
|
decorateWidget(name, fn) {
|
||||||
const widgetName = name.split(":")[0];
|
const widgetName = name.split(":")[0];
|
||||||
this.#deprecatedHeaderWidgetOverride(widgetName, "decorateWidget");
|
this.#deprecatedWidgetOverride(widgetName, "decorateWidget");
|
||||||
|
|
||||||
decorateWidget(name, fn);
|
decorateWidget(name, fn);
|
||||||
}
|
}
|
||||||
|
@ -763,7 +745,7 @@ class PluginApi {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#deprecatedHeaderWidgetOverride(widget, "attachWidgetAction");
|
this.#deprecatedWidgetOverride(widget, "attachWidgetAction");
|
||||||
|
|
||||||
widgetClass.prototype[actionName] = fn;
|
widgetClass.prototype[actionName] = fn;
|
||||||
}
|
}
|
||||||
|
@ -1108,7 +1090,7 @@ class PluginApi {
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
changeWidgetSetting(widgetName, settingName, newValue) {
|
changeWidgetSetting(widgetName, settingName, newValue) {
|
||||||
this.#deprecatedHeaderWidgetOverride(widgetName, "changeWidgetSetting");
|
this.#deprecatedWidgetOverride(widgetName, "changeWidgetSetting");
|
||||||
changeSetting(widgetName, settingName, newValue);
|
changeSetting(widgetName, settingName, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1142,7 +1124,7 @@ class PluginApi {
|
||||||
**/
|
**/
|
||||||
|
|
||||||
reopenWidget(name, args) {
|
reopenWidget(name, args) {
|
||||||
this.#deprecatedHeaderWidgetOverride(name, "reopenWidget");
|
this.#deprecatedWidgetOverride(name, "reopenWidget");
|
||||||
return reopenWidget(name, args);
|
return reopenWidget(name, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,16 +1151,12 @@ class PluginApi {
|
||||||
* and returns a hash of values to pass to attach
|
* and returns a hash of values to pass to attach
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
addHeaderPanel(name, toggle, transformAttrs) {
|
addHeaderPanel() {
|
||||||
deprecated(
|
// eslint-disable-next-line no-console
|
||||||
"addHeaderPanel will be removed as part of the glimmer header upgrade. Use api.headerIcons instead.",
|
console.error(
|
||||||
{
|
consolePrefix(),
|
||||||
id: "discourse.add-header-panel",
|
`api.addHeaderPanel: This API was decommissioned. Use api.headerIcons instead.`
|
||||||
url: "https://meta.discourse.org/t/296544",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
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) {
|
addToHeaderIcons(icon) {
|
||||||
deprecated(
|
// eslint-disable-next-line no-console
|
||||||
"addToHeaderIcons has been deprecated. Use api.headerIcons instead.",
|
console.error(
|
||||||
{
|
consolePrefix(),
|
||||||
id: "discourse.add-header-icons",
|
`api.addToHeaderIcons: This API was decommissioned. Use api.headerIcons instead.`
|
||||||
url: "https://meta.discourse.org/t/296544",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.headerIcons.add(
|
|
||||||
icon,
|
|
||||||
<template><MountWidget @widget={{icon}} /></template>,
|
|
||||||
{ before: "search" }
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2397,7 +2368,6 @@ class PluginApi {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
forceDropdownForMenuPanels(classNames) {
|
forceDropdownForMenuPanels(classNames) {
|
||||||
forceDropdownForMenuPanels(classNames);
|
|
||||||
glimmerForceDropdownForMenuPanels(classNames);
|
glimmerForceDropdownForMenuPanels(classNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3282,18 +3252,20 @@ class PluginApi {
|
||||||
addLegacyAboutPageStat(name);
|
addLegacyAboutPageStat(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#deprecatedHeaderWidgetOverride(widgetName, override) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) {
|
#deprecatedWidgetOverride(widgetName, override) {
|
||||||
this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
|
// insert here the code to handle widget deprecations, e.g. for the header widgets we used:
|
||||||
deprecated(
|
// if (DEPRECATED_HEADER_WIDGETS.includes(widgetName)) {
|
||||||
`The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
|
// this.container.lookup("service:header").anyWidgetHeaderOverrides = true;
|
||||||
{
|
// deprecated(
|
||||||
since: "v3.3.0.beta1-dev",
|
// `The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
|
||||||
id: "discourse.header-widget-overrides",
|
// {
|
||||||
url: "https://meta.discourse.org/t/316549",
|
// 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 hamburgerVisible = false;
|
||||||
@tracked userVisible = false;
|
@tracked userVisible = false;
|
||||||
@tracked anyWidgetHeaderOverrides = false;
|
|
||||||
|
|
||||||
#hiders = new TrackedMap();
|
#hiders = new TrackedMap();
|
||||||
|
|
||||||
|
@ -72,32 +71,6 @@ export default class Header extends Service {
|
||||||
return true;
|
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) {
|
registerHider(ref, buttons) {
|
||||||
const validButtons = buttons
|
const validButtons = buttons
|
||||||
.map((button) => {
|
.map((button) => {
|
||||||
|
|
|
@ -12,32 +12,17 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{#if this.showSiteHeader}}
|
{{#if this.showSiteHeader}}
|
||||||
{{#if this.header.useGlimmerHeader}}
|
<GlimmerSiteHeader
|
||||||
<GlimmerSiteHeader
|
@canSignUp={{this.canSignUp}}
|
||||||
@canSignUp={{this.canSignUp}}
|
@showCreateAccount={{route-action "showCreateAccount"}}
|
||||||
@showCreateAccount={{route-action "showCreateAccount"}}
|
@showLogin={{route-action "showLogin"}}
|
||||||
@showLogin={{route-action "showLogin"}}
|
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
||||||
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
|
@toggleMobileView={{route-action "toggleMobileView"}}
|
||||||
@toggleMobileView={{route-action "toggleMobileView"}}
|
@logout={{route-action "logout"}}
|
||||||
@logout={{route-action "logout"}}
|
@sidebarEnabled={{this.sidebarEnabled}}
|
||||||
@sidebarEnabled={{this.sidebarEnabled}}
|
@showSidebar={{this.showSidebar}}
|
||||||
@showSidebar={{this.showSidebar}}
|
@toggleSidebar={{this.toggleSidebar}}
|
||||||
@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}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<SoftwareUpdatePrompt />
|
<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
|
// TODO: Consolidate these tests into a single acceptance test once the Glimmer
|
||||||
// header is the default.
|
// 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 () {
|
acceptance("Header API - anonymous", function () {
|
||||||
test("can add buttons to the header", async function (assert) {
|
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) {
|
acceptance("Glimmer Header API - authenticated", function (needs) {
|
||||||
needs.user({ groups: AUTO_GROUPS.everyone });
|
needs.user({ groups: AUTO_GROUPS.everyone });
|
||||||
needs.settings({
|
|
||||||
glimmer_header_mode: "enabled",
|
|
||||||
});
|
|
||||||
|
|
||||||
test("can add buttons to the header", async function (assert) {
|
test("can add buttons to the header", async function (assert) {
|
||||||
withPluginApi("1.29.0", (api) => {
|
withPluginApi("1.29.0", (api) => {
|
||||||
|
|
|
@ -65,9 +65,6 @@ async function triggerSwipeEnd({ x, y, touchTarget }) {
|
||||||
acceptance("Mobile - menu swipes", function (needs) {
|
acceptance("Mobile - menu swipes", function (needs) {
|
||||||
needs.mobileView();
|
needs.mobileView();
|
||||||
needs.user();
|
needs.user();
|
||||||
needs.settings({
|
|
||||||
glimmer_header_mode: "enabled",
|
|
||||||
});
|
|
||||||
|
|
||||||
chromeTest("swipe to close hamburger", async function (assert) {
|
chromeTest("swipe to close hamburger", async function (assert) {
|
||||||
await visit("/");
|
await visit("/");
|
||||||
|
|
|
@ -89,10 +89,6 @@ import {
|
||||||
currentSettings,
|
currentSettings,
|
||||||
mergeSettings,
|
mergeSettings,
|
||||||
} from "discourse/tests/helpers/site-settings";
|
} from "discourse/tests/helpers/site-settings";
|
||||||
import {
|
|
||||||
clearExtraHeaderButtons,
|
|
||||||
clearExtraHeaderIcons,
|
|
||||||
} from "discourse/widgets/header";
|
|
||||||
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
||||||
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
|
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
|
||||||
import { resetDecorators } from "discourse/widgets/widget";
|
import { resetDecorators } from "discourse/widgets/widget";
|
||||||
|
@ -236,8 +232,6 @@ export function testCleanup(container, app) {
|
||||||
resetSidebarPanels();
|
resetSidebarPanels();
|
||||||
clearExtraGlimmerHeaderIcons();
|
clearExtraGlimmerHeaderIcons();
|
||||||
clearExtraGlimmerHeaderButtons();
|
clearExtraGlimmerHeaderButtons();
|
||||||
clearExtraHeaderIcons();
|
|
||||||
clearExtraHeaderButtons();
|
|
||||||
resetOnKeyUpCallbacks();
|
resetOnKeyUpCallbacks();
|
||||||
resetLogSearchLinkClickedCallbacks();
|
resetLogSearchLinkClickedCallbacks();
|
||||||
resetItemSelectCallbacks();
|
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_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"
|
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."
|
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_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>."
|
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."
|
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:
|
instrument_gc_stat_per_request:
|
||||||
default: false
|
default: false
|
||||||
hidden: true
|
hidden: true
|
||||||
glimmer_header_mode:
|
|
||||||
client: true
|
|
||||||
type: enum
|
|
||||||
choices:
|
|
||||||
- disabled
|
|
||||||
- auto
|
|
||||||
- enabled
|
|
||||||
default: enabled
|
|
||||||
admin_sidebar_enabled_groups:
|
admin_sidebar_enabled_groups:
|
||||||
type: group_list
|
type: group_list
|
||||||
list_type: compact
|
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 }
|
let(:search) { PageObjects::Pages::Search.new }
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
fab!(:topic)
|
fab!(:topic)
|
||||||
before { SiteSetting.glimmer_header_mode = "enabled" }
|
|
||||||
|
|
||||||
it "renders basics" do
|
it "renders basics" do
|
||||||
visit "/"
|
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