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

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

View File

@ -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}}