DEV: Move rerender on 'do not disturb' change to mixin (#11529)
This commit is contained in:
parent
649ed24bb4
commit
d8e2b497f7
|
@ -6,404 +6,387 @@ import PanEvents, {
|
||||||
import { cancel, later, schedule } from "@ember/runloop";
|
import { cancel, later, schedule } from "@ember/runloop";
|
||||||
import Docking from "discourse/mixins/docking";
|
import Docking from "discourse/mixins/docking";
|
||||||
import MountWidget from "discourse/components/mount-widget";
|
import MountWidget from "discourse/components/mount-widget";
|
||||||
import { isTesting } from "discourse-common/config/environment";
|
import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change";
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
import { observes } from "discourse-common/utils/decorators";
|
||||||
import { topicTitleDecorators } from "discourse/components/topic-title";
|
import { topicTitleDecorators } from "discourse/components/topic-title";
|
||||||
|
|
||||||
const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, {
|
const SiteHeaderComponent = MountWidget.extend(
|
||||||
widget: "header",
|
Docking,
|
||||||
docAt: null,
|
PanEvents,
|
||||||
dockedHeader: null,
|
RerenderOnDoNotDisturbChange,
|
||||||
_listenToDoNotDisturbLoop: null,
|
{
|
||||||
_animate: false,
|
widget: "header",
|
||||||
_isPanning: false,
|
docAt: null,
|
||||||
_panMenuOrigin: "right",
|
dockedHeader: null,
|
||||||
_panMenuOffset: 0,
|
_animate: false,
|
||||||
_scheduledMovingAnimation: null,
|
_isPanning: false,
|
||||||
_scheduledRemoveAnimate: null,
|
_panMenuOrigin: "right",
|
||||||
_topic: null,
|
_panMenuOffset: 0,
|
||||||
|
_scheduledMovingAnimation: null,
|
||||||
|
_scheduledRemoveAnimate: null,
|
||||||
|
_topic: null,
|
||||||
|
|
||||||
@observes(
|
@observes(
|
||||||
"currentUser.unread_notifications",
|
"currentUser.unread_notifications",
|
||||||
"currentUser.unread_high_priority_notifications",
|
"currentUser.unread_high_priority_notifications",
|
||||||
"currentUser.reviewable_count"
|
"currentUser.reviewable_count"
|
||||||
)
|
)
|
||||||
notificationsChanged() {
|
notificationsChanged() {
|
||||||
this.queueRerender();
|
|
||||||
},
|
|
||||||
|
|
||||||
_animateOpening($panel) {
|
|
||||||
$panel.css({ right: "", left: "" });
|
|
||||||
this._panMenuOffset = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
_animateClosing($panel, menuOrigin, windowWidth) {
|
|
||||||
$panel.css(menuOrigin, -windowWidth);
|
|
||||||
this._animate = true;
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
this.eventDispatched("dom:clean", "header");
|
|
||||||
this._panMenuOffset = 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_isRTL() {
|
|
||||||
return $("html").css("direction") === "rtl";
|
|
||||||
},
|
|
||||||
|
|
||||||
_leftMenuClass() {
|
|
||||||
return this._isRTL() ? ".user-menu" : ".hamburger-panel";
|
|
||||||
},
|
|
||||||
|
|
||||||
_leftMenuAction() {
|
|
||||||
return this._isRTL() ? "toggleUserMenu" : "toggleHamburger";
|
|
||||||
},
|
|
||||||
|
|
||||||
_rightMenuAction() {
|
|
||||||
return this._isRTL() ? "toggleHamburger" : "toggleUserMenu";
|
|
||||||
},
|
|
||||||
|
|
||||||
_handlePanDone(offset, event) {
|
|
||||||
const $window = $(window);
|
|
||||||
const windowWidth = $window.width();
|
|
||||||
const $menuPanels = $(".menu-panel");
|
|
||||||
const menuOrigin = this._panMenuOrigin;
|
|
||||||
this._shouldMenuClose(event, menuOrigin)
|
|
||||||
? (offset += SWIPE_VELOCITY)
|
|
||||||
: (offset -= SWIPE_VELOCITY);
|
|
||||||
$menuPanels.each((idx, panel) => {
|
|
||||||
const $panel = $(panel);
|
|
||||||
const $headerCloak = $(".header-cloak");
|
|
||||||
$panel.css(menuOrigin, -offset);
|
|
||||||
$headerCloak.css("opacity", Math.min(0.5, (300 - offset) / 600));
|
|
||||||
if (offset > windowWidth) {
|
|
||||||
this._animateClosing($panel, menuOrigin, windowWidth);
|
|
||||||
} else if (offset <= 0) {
|
|
||||||
this._animateOpening($panel);
|
|
||||||
} else {
|
|
||||||
//continue to open or close menu
|
|
||||||
this._scheduledMovingAnimation = window.requestAnimationFrame(() =>
|
|
||||||
this._handlePanDone(offset, event)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_shouldMenuClose(e, menuOrigin) {
|
|
||||||
// menu should close after a pan either:
|
|
||||||
// if a user moved the panel closed past a threshold and away and is NOT swiping back open
|
|
||||||
// if a user swiped to close fast enough regardless of distance
|
|
||||||
if (menuOrigin === "right") {
|
|
||||||
return (
|
|
||||||
(e.deltaX > SWIPE_DISTANCE_THRESHOLD &&
|
|
||||||
e.velocityX > -SWIPE_VELOCITY_THRESHOLD) ||
|
|
||||||
e.velocityX > 0
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
(e.deltaX < -SWIPE_DISTANCE_THRESHOLD &&
|
|
||||||
e.velocityX < SWIPE_VELOCITY_THRESHOLD) ||
|
|
||||||
e.velocityX < 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
panStart(e) {
|
|
||||||
const center = e.center;
|
|
||||||
const $centeredElement = $(document.elementFromPoint(center.x, center.y));
|
|
||||||
if (
|
|
||||||
($centeredElement.hasClass("panel-body") ||
|
|
||||||
$centeredElement.hasClass("header-cloak") ||
|
|
||||||
$centeredElement.parents(".panel-body").length) &&
|
|
||||||
(e.direction === "left" || e.direction === "right")
|
|
||||||
) {
|
|
||||||
e.originalEvent.preventDefault();
|
|
||||||
this._isPanning = true;
|
|
||||||
} else {
|
|
||||||
this._isPanning = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
panEnd(e) {
|
|
||||||
if (!this._isPanning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._isPanning = false;
|
|
||||||
$(".menu-panel").each((idx, panel) => {
|
|
||||||
const $panel = $(panel);
|
|
||||||
let offset = $panel.css("right");
|
|
||||||
if (this._panMenuOrigin === "left") {
|
|
||||||
offset = $panel.css("left");
|
|
||||||
}
|
|
||||||
offset = Math.abs(parseInt(offset, 10));
|
|
||||||
this._handlePanDone(offset, e);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
panMove(e) {
|
|
||||||
if (!this._isPanning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const $menuPanels = $(".menu-panel");
|
|
||||||
$menuPanels.each((idx, panel) => {
|
|
||||||
const $panel = $(panel);
|
|
||||||
const $headerCloak = $(".header-cloak");
|
|
||||||
if (this._panMenuOrigin === "right") {
|
|
||||||
const pxClosed = Math.min(0, -e.deltaX + this._panMenuOffset);
|
|
||||||
$panel.css("right", pxClosed);
|
|
||||||
$headerCloak.css("opacity", Math.min(0.5, (300 + pxClosed) / 600));
|
|
||||||
} else {
|
|
||||||
const pxClosed = Math.min(0, e.deltaX + this._panMenuOffset);
|
|
||||||
$panel.css("left", pxClosed);
|
|
||||||
$headerCloak.css("opacity", Math.min(0.5, (300 + pxClosed) / 600));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
dockCheck(info) {
|
|
||||||
const $header = $("header.d-header");
|
|
||||||
|
|
||||||
if (this.docAt === null) {
|
|
||||||
if (!($header && $header.length === 1)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.docAt = $header.offset().top;
|
|
||||||
}
|
|
||||||
|
|
||||||
const $body = $("body");
|
|
||||||
const offset = info.offset();
|
|
||||||
if (offset >= this.docAt) {
|
|
||||||
if (!this.dockedHeader) {
|
|
||||||
$body.addClass("docked");
|
|
||||||
this.dockedHeader = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.dockedHeader) {
|
|
||||||
$body.removeClass("docked");
|
|
||||||
this.dockedHeader = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setTopic(topic) {
|
|
||||||
this.eventDispatched("dom:clean", "header");
|
|
||||||
this._topic = topic;
|
|
||||||
this.queueRerender();
|
|
||||||
},
|
|
||||||
|
|
||||||
willRender() {
|
|
||||||
if (this.get("currentUser.staff")) {
|
|
||||||
$("body").addClass("staff");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
listenForDoNotDisturbChanges() {
|
|
||||||
if (this.currentUser && !this.currentUser.isInDoNotDisturb()) {
|
|
||||||
this.queueRerender();
|
this.queueRerender();
|
||||||
} else {
|
},
|
||||||
cancel(this._listenToDoNotDisturbLoop);
|
|
||||||
this._listenToDoNotDisturbLoop = later(
|
|
||||||
this,
|
|
||||||
() => {
|
|
||||||
this.listenForDoNotDisturbChanges();
|
|
||||||
},
|
|
||||||
10000
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
_animateOpening($panel) {
|
||||||
this._super(...arguments);
|
$panel.css({ right: "", left: "" });
|
||||||
$(window).on("resize.discourse-menu-panel", () => this.afterRender());
|
this._panMenuOffset = 0;
|
||||||
|
},
|
||||||
|
|
||||||
this.appEvents.on("header:show-topic", this, "setTopic");
|
_animateClosing($panel, menuOrigin, windowWidth) {
|
||||||
this.appEvents.on("header:hide-topic", this, "setTopic");
|
$panel.css(menuOrigin, -windowWidth);
|
||||||
this.appEvents.on("do-not-disturb:changed", () => this.queueRerender());
|
this._animate = true;
|
||||||
|
schedule("afterRender", () => {
|
||||||
|
this.eventDispatched("dom:clean", "header");
|
||||||
|
this._panMenuOffset = 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
if (!isTesting()) {
|
_isRTL() {
|
||||||
this.listenForDoNotDisturbChanges();
|
return $("html").css("direction") === "rtl";
|
||||||
}
|
},
|
||||||
|
|
||||||
this.dispatch("notifications:changed", "user-notifications");
|
_leftMenuClass() {
|
||||||
this.dispatch("header:keyboard-trigger", "header");
|
return this._isRTL() ? ".user-menu" : ".hamburger-panel";
|
||||||
this.dispatch("search-autocomplete:after-complete", "search-term");
|
},
|
||||||
|
|
||||||
this.appEvents.on("dom:clean", this, "_cleanDom");
|
_leftMenuAction() {
|
||||||
|
return this._isRTL() ? "toggleUserMenu" : "toggleHamburger";
|
||||||
|
},
|
||||||
|
|
||||||
// Allow first notification to be dismissed on a click anywhere
|
_rightMenuAction() {
|
||||||
if (
|
return this._isRTL() ? "toggleHamburger" : "toggleUserMenu";
|
||||||
this.currentUser &&
|
},
|
||||||
!this.get("currentUser.read_first_notification") &&
|
|
||||||
!this.get("currentUser.enforcedSecondFactor")
|
_handlePanDone(offset, event) {
|
||||||
) {
|
const $window = $(window);
|
||||||
this._dismissFirstNotification = (e) => {
|
const windowWidth = $window.width();
|
||||||
if (
|
const $menuPanels = $(".menu-panel");
|
||||||
!e.target.closest("#current-user") &&
|
const menuOrigin = this._panMenuOrigin;
|
||||||
!e.target.closest(".ring-backdrop") &&
|
this._shouldMenuClose(event, menuOrigin)
|
||||||
this.currentUser &&
|
? (offset += SWIPE_VELOCITY)
|
||||||
!this.get("currentUser.read_first_notification") &&
|
: (offset -= SWIPE_VELOCITY);
|
||||||
!this.get("currentUser.enforcedSecondFactor")
|
$menuPanels.each((idx, panel) => {
|
||||||
) {
|
const $panel = $(panel);
|
||||||
this.eventDispatched(
|
const $headerCloak = $(".header-cloak");
|
||||||
"header:dismiss-first-notification-mask",
|
$panel.css(menuOrigin, -offset);
|
||||||
"header"
|
$headerCloak.css("opacity", Math.min(0.5, (300 - offset) / 600));
|
||||||
|
if (offset > windowWidth) {
|
||||||
|
this._animateClosing($panel, menuOrigin, windowWidth);
|
||||||
|
} else if (offset <= 0) {
|
||||||
|
this._animateOpening($panel);
|
||||||
|
} else {
|
||||||
|
//continue to open or close menu
|
||||||
|
this._scheduledMovingAnimation = window.requestAnimationFrame(() =>
|
||||||
|
this._handlePanDone(offset, event)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
document.addEventListener("click", this._dismissFirstNotification, {
|
|
||||||
once: true,
|
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
_cleanDom() {
|
_shouldMenuClose(e, menuOrigin) {
|
||||||
// For performance, only trigger a re-render if any menu panels are visible
|
// menu should close after a pan either:
|
||||||
if (this.element.querySelector(".menu-panel")) {
|
// if a user moved the panel closed past a threshold and away and is NOT swiping back open
|
||||||
this.eventDispatched("dom:clean", "header");
|
// if a user swiped to close fast enough regardless of distance
|
||||||
}
|
if (menuOrigin === "right") {
|
||||||
},
|
return (
|
||||||
|
(e.deltaX > SWIPE_DISTANCE_THRESHOLD &&
|
||||||
willDestroyElement() {
|
e.velocityX > -SWIPE_VELOCITY_THRESHOLD) ||
|
||||||
this._super(...arguments);
|
e.velocityX > 0
|
||||||
$("body").off("keydown.header");
|
);
|
||||||
$(window).off("resize.discourse-menu-panel");
|
} else {
|
||||||
|
return (
|
||||||
this.appEvents.off("header:show-topic", this, "setTopic");
|
(e.deltaX < -SWIPE_DISTANCE_THRESHOLD &&
|
||||||
this.appEvents.off("header:hide-topic", this, "setTopic");
|
e.velocityX < SWIPE_VELOCITY_THRESHOLD) ||
|
||||||
this.appEvents.off("dom:clean", this, "_cleanDom");
|
e.velocityX < 0
|
||||||
|
);
|
||||||
cancel(this._scheduledRemoveAnimate);
|
|
||||||
cancel(this._listenToDoNotDisturbLoop);
|
|
||||||
window.cancelAnimationFrame(this._scheduledMovingAnimation);
|
|
||||||
|
|
||||||
document.removeEventListener("click", this._dismissFirstNotification);
|
|
||||||
},
|
|
||||||
|
|
||||||
buildArgs() {
|
|
||||||
return {
|
|
||||||
topic: this._topic,
|
|
||||||
canSignUp: this.canSignUp,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender() {
|
|
||||||
const headerTitle = document.querySelector(".header-title .topic-link");
|
|
||||||
if (headerTitle && this._topic) {
|
|
||||||
topicTitleDecorators.forEach((cb) =>
|
|
||||||
cb(this._topic, headerTitle, "header-title")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const $menuPanels = $(".menu-panel");
|
|
||||||
if ($menuPanels.length === 0) {
|
|
||||||
if (this.site.mobileView) {
|
|
||||||
this._animate = true;
|
|
||||||
}
|
}
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
const $window = $(window);
|
panStart(e) {
|
||||||
const windowWidth = $window.width();
|
const center = e.center;
|
||||||
|
const $centeredElement = $(document.elementFromPoint(center.x, center.y));
|
||||||
const headerWidth = $("#main-outlet .container").width() || 1100;
|
if (
|
||||||
const remaining = (windowWidth - headerWidth) / 2;
|
($centeredElement.hasClass("panel-body") ||
|
||||||
const viewMode = remaining < 50 ? "slide-in" : "drop-down";
|
$centeredElement.hasClass("header-cloak") ||
|
||||||
|
$centeredElement.parents(".panel-body").length) &&
|
||||||
$menuPanels.each((idx, panel) => {
|
(e.direction === "left" || e.direction === "right")
|
||||||
const $panel = $(panel);
|
) {
|
||||||
const $headerCloak = $(".header-cloak");
|
e.originalEvent.preventDefault();
|
||||||
let width = parseInt($panel.attr("data-max-width"), 10) || 300;
|
this._isPanning = true;
|
||||||
if (windowWidth - width < 50) {
|
} else {
|
||||||
width = windowWidth - 50;
|
this._isPanning = false;
|
||||||
}
|
|
||||||
if (this._panMenuOffset) {
|
|
||||||
this._panMenuOffset = -width;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
$panel.removeClass("drop-down slide-in").addClass(viewMode);
|
panEnd(e) {
|
||||||
if (this._animate || this._panMenuOffset !== 0) {
|
if (!this._isPanning) {
|
||||||
$headerCloak.css("opacity", 0);
|
return;
|
||||||
if (
|
}
|
||||||
this.site.mobileView &&
|
this._isPanning = false;
|
||||||
$panel.parent(this._leftMenuClass()).length > 0
|
$(".menu-panel").each((idx, panel) => {
|
||||||
) {
|
const $panel = $(panel);
|
||||||
this._panMenuOrigin = "left";
|
let offset = $panel.css("right");
|
||||||
$panel.css("left", -windowWidth);
|
if (this._panMenuOrigin === "left") {
|
||||||
} else {
|
offset = $panel.css("left");
|
||||||
this._panMenuOrigin = "right";
|
|
||||||
$panel.css("right", -windowWidth);
|
|
||||||
}
|
}
|
||||||
|
offset = Math.abs(parseInt(offset, 10));
|
||||||
|
this._handlePanDone(offset, e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
panMove(e) {
|
||||||
|
if (!this._isPanning) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const $menuPanels = $(".menu-panel");
|
||||||
|
$menuPanels.each((idx, panel) => {
|
||||||
|
const $panel = $(panel);
|
||||||
|
const $headerCloak = $(".header-cloak");
|
||||||
|
if (this._panMenuOrigin === "right") {
|
||||||
|
const pxClosed = Math.min(0, -e.deltaX + this._panMenuOffset);
|
||||||
|
$panel.css("right", pxClosed);
|
||||||
|
$headerCloak.css("opacity", Math.min(0.5, (300 + pxClosed) / 600));
|
||||||
|
} else {
|
||||||
|
const pxClosed = Math.min(0, e.deltaX + this._panMenuOffset);
|
||||||
|
$panel.css("left", pxClosed);
|
||||||
|
$headerCloak.css("opacity", Math.min(0.5, (300 + pxClosed) / 600));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
const $panelBody = $(".panel-body", $panel);
|
dockCheck(info) {
|
||||||
|
const $header = $("header.d-header");
|
||||||
|
|
||||||
// We use a mutationObserver to check for style changes, so it's important
|
if (this.docAt === null) {
|
||||||
// we don't set it if it doesn't change. Same goes for the $panelBody!
|
if (!($header && $header.length === 1)) {
|
||||||
const style = $panel.prop("style");
|
|
||||||
|
|
||||||
if (viewMode === "drop-down") {
|
|
||||||
const $buttonPanel = $("header ul.icons");
|
|
||||||
if ($buttonPanel.length === 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.docAt = $header.offset().top;
|
||||||
|
}
|
||||||
|
|
||||||
// These values need to be set here, not in the css file - this is to deal with the
|
const $body = $("body");
|
||||||
// possibility of the window being resized and the menu changing from .slide-in to .drop-down.
|
const offset = info.offset();
|
||||||
if (style.top !== "100%" || style.height !== "auto") {
|
if (offset >= this.docAt) {
|
||||||
$panel.css({ top: "100%", height: "auto" });
|
if (!this.dockedHeader) {
|
||||||
|
$body.addClass("docked");
|
||||||
|
this.dockedHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("body").addClass("drop-down-mode");
|
|
||||||
} else {
|
} else {
|
||||||
if (this.site.mobileView) {
|
if (this.dockedHeader) {
|
||||||
$headerCloak.show();
|
$body.removeClass("docked");
|
||||||
|
this.dockedHeader = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
const menuTop = this.site.mobileView ? headerTop() : headerHeight();
|
setTopic(topic) {
|
||||||
|
this.eventDispatched("dom:clean", "header");
|
||||||
|
this._topic = topic;
|
||||||
|
this.queueRerender();
|
||||||
|
},
|
||||||
|
|
||||||
const winHeightOffset = 16;
|
willRender() {
|
||||||
let initialWinHeight = window.innerHeight
|
if (this.get("currentUser.staff")) {
|
||||||
? window.innerHeight
|
$("body").addClass("staff");
|
||||||
: $(window).height();
|
}
|
||||||
const winHeight = initialWinHeight - winHeightOffset;
|
},
|
||||||
|
|
||||||
let height;
|
didInsertElement() {
|
||||||
if (this.site.mobileView) {
|
this._super(...arguments);
|
||||||
height = winHeight - menuTop;
|
$(window).on("resize.discourse-menu-panel", () => this.afterRender());
|
||||||
}
|
|
||||||
|
|
||||||
const isIPadApp = document.body.classList.contains("footer-nav-ipad"),
|
this.appEvents.on("header:show-topic", this, "setTopic");
|
||||||
heightProp = isIPadApp ? "max-height" : "height",
|
this.appEvents.on("header:hide-topic", this, "setTopic");
|
||||||
iPadOffset = 10;
|
|
||||||
|
|
||||||
if (isIPadApp) {
|
this.dispatch("notifications:changed", "user-notifications");
|
||||||
height = winHeight - menuTop - iPadOffset;
|
this.dispatch("header:keyboard-trigger", "header");
|
||||||
}
|
this.dispatch("search-autocomplete:after-complete", "search-term");
|
||||||
|
|
||||||
if ($panelBody.prop("style").height !== "100%") {
|
this.appEvents.on("dom:clean", this, "_cleanDom");
|
||||||
$panelBody.height("100%");
|
|
||||||
}
|
// Allow first notification to be dismissed on a click anywhere
|
||||||
if (style.top !== menuTop + "px" || style[heightProp] !== height) {
|
if (
|
||||||
$panel.css({ top: menuTop + "px", [heightProp]: height });
|
this.currentUser &&
|
||||||
$(".header-cloak").css({ top: menuTop + "px" });
|
!this.get("currentUser.read_first_notification") &&
|
||||||
}
|
!this.get("currentUser.enforcedSecondFactor")
|
||||||
$("body").removeClass("drop-down-mode");
|
) {
|
||||||
|
this._dismissFirstNotification = (e) => {
|
||||||
|
if (
|
||||||
|
!e.target.closest("#current-user") &&
|
||||||
|
!e.target.closest(".ring-backdrop") &&
|
||||||
|
this.currentUser &&
|
||||||
|
!this.get("currentUser.read_first_notification") &&
|
||||||
|
!this.get("currentUser.enforcedSecondFactor")
|
||||||
|
) {
|
||||||
|
this.eventDispatched(
|
||||||
|
"header:dismiss-first-notification-mask",
|
||||||
|
"header"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("click", this._dismissFirstNotification, {
|
||||||
|
once: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_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);
|
||||||
|
$("body").off("keydown.header");
|
||||||
|
$(window).off("resize.discourse-menu-panel");
|
||||||
|
|
||||||
|
this.appEvents.off("header:show-topic", this, "setTopic");
|
||||||
|
this.appEvents.off("header:hide-topic", this, "setTopic");
|
||||||
|
this.appEvents.off("dom:clean", this, "_cleanDom");
|
||||||
|
|
||||||
|
cancel(this._scheduledRemoveAnimate);
|
||||||
|
window.cancelAnimationFrame(this._scheduledMovingAnimation);
|
||||||
|
|
||||||
|
document.removeEventListener("click", this._dismissFirstNotification);
|
||||||
|
},
|
||||||
|
|
||||||
|
buildArgs() {
|
||||||
|
return {
|
||||||
|
topic: this._topic,
|
||||||
|
canSignUp: this.canSignUp,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRender() {
|
||||||
|
const headerTitle = document.querySelector(".header-title .topic-link");
|
||||||
|
if (headerTitle && this._topic) {
|
||||||
|
topicTitleDecorators.forEach((cb) =>
|
||||||
|
cb(this._topic, headerTitle, "header-title")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$panel.width(width);
|
const $menuPanels = $(".menu-panel");
|
||||||
if (this._animate) {
|
if ($menuPanels.length === 0) {
|
||||||
$panel.addClass("animate");
|
if (this.site.mobileView) {
|
||||||
$headerCloak.addClass("animate");
|
this._animate = true;
|
||||||
this._scheduledRemoveAnimate = later(() => {
|
}
|
||||||
$panel.removeClass("animate");
|
return;
|
||||||
$headerCloak.removeClass("animate");
|
|
||||||
}, 200);
|
|
||||||
}
|
}
|
||||||
$panel.css({ right: "", left: "" });
|
|
||||||
$headerCloak.css("opacity", 0.5);
|
const $window = $(window);
|
||||||
this._animate = false;
|
const windowWidth = $window.width();
|
||||||
});
|
|
||||||
},
|
const headerWidth = $("#main-outlet .container").width() || 1100;
|
||||||
});
|
const remaining = (windowWidth - headerWidth) / 2;
|
||||||
|
const viewMode = remaining < 50 ? "slide-in" : "drop-down";
|
||||||
|
|
||||||
|
$menuPanels.each((idx, panel) => {
|
||||||
|
const $panel = $(panel);
|
||||||
|
const $headerCloak = $(".header-cloak");
|
||||||
|
let width = parseInt($panel.attr("data-max-width"), 10) || 300;
|
||||||
|
if (windowWidth - width < 50) {
|
||||||
|
width = windowWidth - 50;
|
||||||
|
}
|
||||||
|
if (this._panMenuOffset) {
|
||||||
|
this._panMenuOffset = -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
$panel.removeClass("drop-down slide-in").addClass(viewMode);
|
||||||
|
if (this._animate || this._panMenuOffset !== 0) {
|
||||||
|
$headerCloak.css("opacity", 0);
|
||||||
|
if (
|
||||||
|
this.site.mobileView &&
|
||||||
|
$panel.parent(this._leftMenuClass()).length > 0
|
||||||
|
) {
|
||||||
|
this._panMenuOrigin = "left";
|
||||||
|
$panel.css("left", -windowWidth);
|
||||||
|
} else {
|
||||||
|
this._panMenuOrigin = "right";
|
||||||
|
$panel.css("right", -windowWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $panelBody = $(".panel-body", $panel);
|
||||||
|
|
||||||
|
// We use a mutationObserver to check for style changes, so it's important
|
||||||
|
// we don't set it if it doesn't change. Same goes for the $panelBody!
|
||||||
|
const style = $panel.prop("style");
|
||||||
|
|
||||||
|
if (viewMode === "drop-down") {
|
||||||
|
const $buttonPanel = $("header ul.icons");
|
||||||
|
if ($buttonPanel.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These values need to be set here, not in the css file - this is to deal with the
|
||||||
|
// possibility of the window being resized and the menu changing from .slide-in to .drop-down.
|
||||||
|
if (style.top !== "100%" || style.height !== "auto") {
|
||||||
|
$panel.css({ top: "100%", height: "auto" });
|
||||||
|
}
|
||||||
|
|
||||||
|
$("body").addClass("drop-down-mode");
|
||||||
|
} else {
|
||||||
|
if (this.site.mobileView) {
|
||||||
|
$headerCloak.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuTop = this.site.mobileView ? headerTop() : headerHeight();
|
||||||
|
|
||||||
|
const winHeightOffset = 16;
|
||||||
|
let initialWinHeight = window.innerHeight
|
||||||
|
? window.innerHeight
|
||||||
|
: $(window).height();
|
||||||
|
const winHeight = initialWinHeight - winHeightOffset;
|
||||||
|
|
||||||
|
let height;
|
||||||
|
if (this.site.mobileView) {
|
||||||
|
height = winHeight - menuTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isIPadApp = document.body.classList.contains("footer-nav-ipad"),
|
||||||
|
heightProp = isIPadApp ? "max-height" : "height",
|
||||||
|
iPadOffset = 10;
|
||||||
|
|
||||||
|
if (isIPadApp) {
|
||||||
|
height = winHeight - menuTop - iPadOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($panelBody.prop("style").height !== "100%") {
|
||||||
|
$panelBody.height("100%");
|
||||||
|
}
|
||||||
|
if (style.top !== menuTop + "px" || style[heightProp] !== height) {
|
||||||
|
$panel.css({ top: menuTop + "px", [heightProp]: height });
|
||||||
|
$(".header-cloak").css({ top: menuTop + "px" });
|
||||||
|
}
|
||||||
|
$("body").removeClass("drop-down-mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
$panel.width(width);
|
||||||
|
if (this._animate) {
|
||||||
|
$panel.addClass("animate");
|
||||||
|
$headerCloak.addClass("animate");
|
||||||
|
this._scheduledRemoveAnimate = later(() => {
|
||||||
|
$panel.removeClass("animate");
|
||||||
|
$headerCloak.removeClass("animate");
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
$panel.css({ right: "", left: "" });
|
||||||
|
$headerCloak.css("opacity", 0.5);
|
||||||
|
this._animate = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default SiteHeaderComponent.extend({
|
export default SiteHeaderComponent.extend({
|
||||||
classNames: ["d-header-wrap"],
|
classNames: ["d-header-wrap"],
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { cancel, later } from "@ember/runloop";
|
||||||
|
import Mixin from "@ember/object/mixin";
|
||||||
|
import { isTesting } from "discourse-common/config/environment";
|
||||||
|
|
||||||
|
export default Mixin.create({
|
||||||
|
_listenToDoNotDisturbLoop: null,
|
||||||
|
|
||||||
|
listenForDoNotDisturbChanges() {
|
||||||
|
if (this.currentUser && !this.currentUser.isInDoNotDisturb()) {
|
||||||
|
this.queueRerender();
|
||||||
|
} else {
|
||||||
|
cancel(this._listenToDoNotDisturbLoop);
|
||||||
|
this._listenToDoNotDisturbLoop = later(
|
||||||
|
this,
|
||||||
|
() => {
|
||||||
|
this.listenForDoNotDisturbChanges();
|
||||||
|
},
|
||||||
|
10000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
this.appEvents.on("do-not-disturb:changed", () => this.queueRerender());
|
||||||
|
if (!isTesting()) {
|
||||||
|
this.listenForDoNotDisturbChanges();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
cancel(this._listenToDoNotDisturbLoop);
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue