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";
|
||||
|
||||
let _cleanCallbacks = {};
|
||||
|
||||
export function addWidgetCleanCallback(widgetName, fn) {
|
||||
_cleanCallbacks[widgetName] = _cleanCallbacks[widgetName] || [];
|
||||
_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() {
|
||||
_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 Site from "discourse/models/site";
|
||||
import Modifier from "ember-modifier";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { registerDestructor } from "@ember/destroyable";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import {
|
||||
addWidgetCleanCallback,
|
||||
removeWidgetCleanCallback,
|
||||
} from "discourse/components/mount-widget";
|
||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||
import { getOwner, setOwner } from "@ember/application";
|
||||
import { schedule } from "@ember/runloop";
|
||||
|
||||
export default class StickyAvatars {
|
||||
static init(owner) {
|
||||
return new this(owner).init();
|
||||
}
|
||||
const STICKY_CLASS = "sticky-avatar";
|
||||
const TOPIC_POST_SELECTOR = ".post-stream .topic-post";
|
||||
|
||||
stickyClass = "sticky-avatar";
|
||||
topicPostSelector = "#topic .post-stream .topic-post";
|
||||
intersectionObserver = null;
|
||||
export default class StickyAvatars extends Modifier {
|
||||
@service site;
|
||||
@service appEvents;
|
||||
|
||||
element;
|
||||
intersectionObserver;
|
||||
direction = "⬇️";
|
||||
prevOffset = -1;
|
||||
|
||||
constructor(owner) {
|
||||
setOwner(this, owner);
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
registerDestructor(this, (instance) => instance.cleanup());
|
||||
}
|
||||
|
||||
init() {
|
||||
if (Site.currentProp("mobileView") || !("IntersectionObserver" in window)) {
|
||||
modify(element) {
|
||||
if (this.site.mobileView || !("IntersectionObserver" in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const appEvents = getOwner(this).lookup("service:app-events");
|
||||
appEvents.on("topic:current-post-scrolled", this._handlePostNodes);
|
||||
appEvents.on("topic:scrolled", this._handleScroll);
|
||||
appEvents.on("page:topic-loaded", this._initIntersectionObserver);
|
||||
this.element = element;
|
||||
|
||||
this.appEvents.on(
|
||||
"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);
|
||||
|
||||
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
|
||||
_handleScroll(offset) {
|
||||
if (offset <= 0) {
|
||||
this.direction = "⬇️";
|
||||
document
|
||||
.querySelectorAll(`${this.topicPostSelector}.${this.stickyClass}`)
|
||||
.forEach((node) => node.classList.remove(this.stickyClass));
|
||||
this.element
|
||||
.querySelectorAll(`${TOPIC_POST_SELECTOR}.${STICKY_CLASS}`)
|
||||
.forEach((node) => node.classList.remove(STICKY_CLASS));
|
||||
} else if (offset > this.prevOffset) {
|
||||
this.direction = "⬇️";
|
||||
} else {
|
||||
this.direction = "⬆️";
|
||||
}
|
||||
|
||||
this.prevOffset = offset;
|
||||
}
|
||||
|
||||
|
@ -58,7 +90,7 @@ export default class StickyAvatars {
|
|||
this._initIntersectionObserver();
|
||||
|
||||
schedule("afterRender", () => {
|
||||
document.querySelectorAll(this.topicPostSelector).forEach((postNode) => {
|
||||
this.element.querySelectorAll(TOPIC_POST_SELECTOR).forEach((postNode) => {
|
||||
this.intersectionObserver.observe(postNode);
|
||||
|
||||
const topicAvatarNode = postNode.querySelector(".topic-avatar");
|
||||
|
@ -70,6 +102,7 @@ export default class StickyAvatars {
|
|||
if (!topicMapNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
topicAvatarNode.style.marginBottom = `${topicMapNode.clientHeight}px`;
|
||||
});
|
||||
});
|
||||
|
@ -78,14 +111,14 @@ export default class StickyAvatars {
|
|||
@bind
|
||||
_initIntersectionObserver() {
|
||||
schedule("afterRender", () => {
|
||||
const headerOffsetInPx =
|
||||
headerOffset() <= 0 ? "0px" : `-${headerOffset()}px`;
|
||||
const offset = headerOffset();
|
||||
const headerOffsetInPx = offset <= 0 ? "0px" : `-${offset}px`;
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting || entry.intersectionRatio === 1) {
|
||||
entry.target.classList.remove(this.stickyClass);
|
||||
entry.target.classList.remove(STICKY_CLASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,9 +126,9 @@ export default class StickyAvatars {
|
|||
entry.target.querySelector(".contents")?.clientHeight;
|
||||
if (
|
||||
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}}
|
||||
|
||||
<DiscourseTopic
|
||||
{{sticky-avatars}}
|
||||
@multiSelect={{this.multiSelect}}
|
||||
@enteredAt={{this.enteredAt}}
|
||||
@topic={{this.model}}
|
||||
|
|
Loading…
Reference in New Issue