DEV: Convert sticky-avatars into a modifier (#23060)
Instead of a initializer and a lib class (that needed access to the ownership system)
This commit is contained in:
parent
e74560f062
commit
5e7287eba5
|
@ -9,11 +9,26 @@ import { getRegister } from "discourse-common/lib/get-owner";
|
||||||
import ArrayProxy from "@ember/array/proxy";
|
import ArrayProxy from "@ember/array/proxy";
|
||||||
|
|
||||||
let _cleanCallbacks = {};
|
let _cleanCallbacks = {};
|
||||||
|
|
||||||
export function addWidgetCleanCallback(widgetName, fn) {
|
export function addWidgetCleanCallback(widgetName, fn) {
|
||||||
_cleanCallbacks[widgetName] = _cleanCallbacks[widgetName] || [];
|
_cleanCallbacks[widgetName] = _cleanCallbacks[widgetName] || [];
|
||||||
_cleanCallbacks[widgetName].push(fn);
|
_cleanCallbacks[widgetName].push(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeWidgetCleanCallback(widgetName, fn) {
|
||||||
|
const callbacks = _cleanCallbacks[widgetName];
|
||||||
|
if (!callbacks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = callbacks.indexOf(fn);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
export function resetWidgetCleanCallbacks() {
|
export function resetWidgetCleanCallbacks() {
|
||||||
_cleanCallbacks = {};
|
_cleanCallbacks = {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import StickyAvatars from "discourse/lib/sticky-avatars";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
after: "inject-objects",
|
|
||||||
|
|
||||||
initialize(owner) {
|
|
||||||
this._stickyAvatars = StickyAvatars.init(owner);
|
|
||||||
},
|
|
||||||
|
|
||||||
teardown() {
|
|
||||||
this._stickyAvatars?.destroy();
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,54 +1,86 @@
|
||||||
import { addWidgetCleanCallback } from "discourse/components/mount-widget";
|
import Modifier from "ember-modifier";
|
||||||
import Site from "discourse/models/site";
|
import { inject as service } from "@ember/service";
|
||||||
|
import { registerDestructor } from "@ember/destroyable";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import {
|
||||||
|
addWidgetCleanCallback,
|
||||||
|
removeWidgetCleanCallback,
|
||||||
|
} from "discourse/components/mount-widget";
|
||||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||||
import { getOwner, setOwner } from "@ember/application";
|
|
||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
|
|
||||||
export default class StickyAvatars {
|
const STICKY_CLASS = "sticky-avatar";
|
||||||
static init(owner) {
|
const TOPIC_POST_SELECTOR = ".post-stream .topic-post";
|
||||||
return new this(owner).init();
|
|
||||||
}
|
|
||||||
|
|
||||||
stickyClass = "sticky-avatar";
|
export default class StickyAvatars extends Modifier {
|
||||||
topicPostSelector = "#topic .post-stream .topic-post";
|
@service site;
|
||||||
intersectionObserver = null;
|
@service appEvents;
|
||||||
|
|
||||||
|
element;
|
||||||
|
intersectionObserver;
|
||||||
direction = "⬇️";
|
direction = "⬇️";
|
||||||
prevOffset = -1;
|
prevOffset = -1;
|
||||||
|
|
||||||
constructor(owner) {
|
constructor() {
|
||||||
setOwner(this, owner);
|
super(...arguments);
|
||||||
|
registerDestructor(this, (instance) => instance.cleanup());
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
modify(element) {
|
||||||
if (Site.currentProp("mobileView") || !("IntersectionObserver" in window)) {
|
if (this.site.mobileView || !("IntersectionObserver" in window)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const appEvents = getOwner(this).lookup("service:app-events");
|
this.element = element;
|
||||||
appEvents.on("topic:current-post-scrolled", this._handlePostNodes);
|
|
||||||
appEvents.on("topic:scrolled", this._handleScroll);
|
this.appEvents.on(
|
||||||
appEvents.on("page:topic-loaded", this._initIntersectionObserver);
|
"topic:current-post-scrolled",
|
||||||
|
this,
|
||||||
|
this._handlePostNodes
|
||||||
|
);
|
||||||
|
this.appEvents.on("topic:scrolled", this, this._handleScroll);
|
||||||
|
this.appEvents.on(
|
||||||
|
"page:topic-loaded",
|
||||||
|
this,
|
||||||
|
this._initIntersectionObserver
|
||||||
|
);
|
||||||
|
|
||||||
addWidgetCleanCallback("post-stream", this._clearIntersectionObserver);
|
addWidgetCleanCallback("post-stream", this._clearIntersectionObserver);
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {}
|
cleanup() {
|
||||||
|
if (this.site.mobileView || !("IntersectionObserver" in window)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.appEvents.off(
|
||||||
|
"topic:current-post-scrolled",
|
||||||
|
this,
|
||||||
|
this._handlePostNodes
|
||||||
|
);
|
||||||
|
this.appEvents.off("topic:scrolled", this, this._handleScroll);
|
||||||
|
this.appEvents.off(
|
||||||
|
"page:topic-loaded",
|
||||||
|
this,
|
||||||
|
this._initIntersectionObserver
|
||||||
|
);
|
||||||
|
|
||||||
|
removeWidgetCleanCallback("post-stream", this._clearIntersectionObserver);
|
||||||
|
}
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
_handleScroll(offset) {
|
_handleScroll(offset) {
|
||||||
if (offset <= 0) {
|
if (offset <= 0) {
|
||||||
this.direction = "⬇️";
|
this.direction = "⬇️";
|
||||||
document
|
this.element
|
||||||
.querySelectorAll(`${this.topicPostSelector}.${this.stickyClass}`)
|
.querySelectorAll(`${TOPIC_POST_SELECTOR}.${STICKY_CLASS}`)
|
||||||
.forEach((node) => node.classList.remove(this.stickyClass));
|
.forEach((node) => node.classList.remove(STICKY_CLASS));
|
||||||
} else if (offset > this.prevOffset) {
|
} else if (offset > this.prevOffset) {
|
||||||
this.direction = "⬇️";
|
this.direction = "⬇️";
|
||||||
} else {
|
} else {
|
||||||
this.direction = "⬆️";
|
this.direction = "⬆️";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.prevOffset = offset;
|
this.prevOffset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +90,7 @@ export default class StickyAvatars {
|
||||||
this._initIntersectionObserver();
|
this._initIntersectionObserver();
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
document.querySelectorAll(this.topicPostSelector).forEach((postNode) => {
|
this.element.querySelectorAll(TOPIC_POST_SELECTOR).forEach((postNode) => {
|
||||||
this.intersectionObserver.observe(postNode);
|
this.intersectionObserver.observe(postNode);
|
||||||
|
|
||||||
const topicAvatarNode = postNode.querySelector(".topic-avatar");
|
const topicAvatarNode = postNode.querySelector(".topic-avatar");
|
||||||
|
@ -70,6 +102,7 @@ export default class StickyAvatars {
|
||||||
if (!topicMapNode) {
|
if (!topicMapNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
topicAvatarNode.style.marginBottom = `${topicMapNode.clientHeight}px`;
|
topicAvatarNode.style.marginBottom = `${topicMapNode.clientHeight}px`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -78,14 +111,14 @@ export default class StickyAvatars {
|
||||||
@bind
|
@bind
|
||||||
_initIntersectionObserver() {
|
_initIntersectionObserver() {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
const headerOffsetInPx =
|
const offset = headerOffset();
|
||||||
headerOffset() <= 0 ? "0px" : `-${headerOffset()}px`;
|
const headerOffsetInPx = offset <= 0 ? "0px" : `-${offset}px`;
|
||||||
|
|
||||||
this.intersectionObserver = new IntersectionObserver(
|
this.intersectionObserver = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (!entry.isIntersecting || entry.intersectionRatio === 1) {
|
if (!entry.isIntersecting || entry.intersectionRatio === 1) {
|
||||||
entry.target.classList.remove(this.stickyClass);
|
entry.target.classList.remove(STICKY_CLASS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +126,9 @@ export default class StickyAvatars {
|
||||||
entry.target.querySelector(".contents")?.clientHeight;
|
entry.target.querySelector(".contents")?.clientHeight;
|
||||||
if (
|
if (
|
||||||
this.direction === "⬆️" ||
|
this.direction === "⬆️" ||
|
||||||
postContentHeight > window.innerHeight - headerOffset()
|
postContentHeight > window.innerHeight - offset
|
||||||
) {
|
) {
|
||||||
entry.target.classList.add(this.stickyClass);
|
entry.target.classList.add(STICKY_CLASS);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
|
@ -5,6 +5,7 @@
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
|
||||||
<DiscourseTopic
|
<DiscourseTopic
|
||||||
|
{{sticky-avatars}}
|
||||||
@multiSelect={{this.multiSelect}}
|
@multiSelect={{this.multiSelect}}
|
||||||
@enteredAt={{this.enteredAt}}
|
@enteredAt={{this.enteredAt}}
|
||||||
@topic={{this.model}}
|
@topic={{this.model}}
|
||||||
|
|
Loading…
Reference in New Issue