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:
Jarek Radosz 2023-09-06 11:32:08 +02:00 committed by GitHub
parent e74560f062
commit 5e7287eba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 43 deletions

View File

@ -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 = {};
}

View File

@ -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();
},
};

View File

@ -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);
}
});
},

View File

@ -5,6 +5,7 @@
{{/let}}
<DiscourseTopic
{{sticky-avatars}}
@multiSelect={{this.multiSelect}}
@enteredAt={{this.enteredAt}}
@topic={{this.model}}