From 5e7287eba5498e237ec19f5b3f6627d8130ff820 Mon Sep 17 00:00:00 2001 From: Jarek Radosz Date: Wed, 6 Sep 2023 11:32:08 +0200 Subject: [PATCH] DEV: Convert sticky-avatars into a modifier (#23060) Instead of a initializer and a lib class (that needed access to the ownership system) --- .../discourse/app/components/mount-widget.js | 15 +++ .../instance-initializers/sticky-avatars.js | 13 --- .../app/{lib => modifiers}/sticky-avatars.js | 93 +++++++++++++------ .../discourse/app/templates/topic.hbs | 1 + 4 files changed, 79 insertions(+), 43 deletions(-) delete mode 100644 app/assets/javascripts/discourse/app/instance-initializers/sticky-avatars.js rename app/assets/javascripts/discourse/app/{lib => modifiers}/sticky-avatars.js (50%) diff --git a/app/assets/javascripts/discourse/app/components/mount-widget.js b/app/assets/javascripts/discourse/app/components/mount-widget.js index d14f255cce5..4fd888112ac 100644 --- a/app/assets/javascripts/discourse/app/components/mount-widget.js +++ b/app/assets/javascripts/discourse/app/components/mount-widget.js @@ -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 = {}; } diff --git a/app/assets/javascripts/discourse/app/instance-initializers/sticky-avatars.js b/app/assets/javascripts/discourse/app/instance-initializers/sticky-avatars.js deleted file mode 100644 index af393e5b2d2..00000000000 --- a/app/assets/javascripts/discourse/app/instance-initializers/sticky-avatars.js +++ /dev/null @@ -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(); - }, -}; diff --git a/app/assets/javascripts/discourse/app/lib/sticky-avatars.js b/app/assets/javascripts/discourse/app/modifiers/sticky-avatars.js similarity index 50% rename from app/assets/javascripts/discourse/app/lib/sticky-avatars.js rename to app/assets/javascripts/discourse/app/modifiers/sticky-avatars.js index 16fd8af2b74..93fb00d6cb3 100644 --- a/app/assets/javascripts/discourse/app/lib/sticky-avatars.js +++ b/app/assets/javascripts/discourse/app/modifiers/sticky-avatars.js @@ -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); } }); }, diff --git a/app/assets/javascripts/discourse/app/templates/topic.hbs b/app/assets/javascripts/discourse/app/templates/topic.hbs index 6e3685c223a..c63b1a8bd56 100644 --- a/app/assets/javascripts/discourse/app/templates/topic.hbs +++ b/app/assets/javascripts/discourse/app/templates/topic.hbs @@ -5,6 +5,7 @@ {{/let}}