FIX: Decorate posts that are loaded after the initial render in post stream (#14600)

FIX: Decorate posts that are loaded after the initial render in post stream (#14600)

To clarify, this problem is not about the topic posts stream, it’s about posts streams like the user Activity one in the profile page (or in technical terms anything using the {{user-stream}} component).

Post decorations are currently applied inside a didInsertElement hook of the {{user-stream}} component. However, when the user scrolls the component will load more posts but these will be missing decorations because the didInsertElement is only fired once at the beginning of the component lifecycle.

This PR makes the component keep track of the last decorated post/DOM node, and when new posts are loaded the component fire an event for each new post and pass the post’s DOM node with the event. Our plugin API

(I noticed this problem when I was working on FIX: Apply post decorations to posts shown in the follow feed by OsamaSayegh · Pull Request #37 · discourse/discourse-follow · GitHub)

Co-authored-by: Robin Ward robin.ward@gmail.com

diff --git a/app/assets/javascripts/discourse/app/components/user-stream.js b/app/assets/javascripts/discourse/app/components/user-stream.js
index 3d1c1e6..0632ab4 100644
--- a/app/assets/javascripts/discourse/app/components/user-stream.js
+++ b/app/assets/javascripts/discourse/app/components/user-stream.js
@@ -14,6 +14,7 @@ import { schedule } from "@ember/runloop";
 
 export default Component.extend(LoadMore, {
   tagName: "ul",
+  _lastDecoratedElement: null,
 
   _initialize: on("init", function () {
     const filter = this.get("stream.filter");
@@ -47,6 +48,7 @@ export default Component.extend(LoadMore, {
     $(this.element).on("click.discourse-redirect", ".excerpt a", (e) => {
       return ClickTrack.trackClick(e, this.siteSettings);
     });
+    this._updateLastDecoratedElement();
   }),
 
   // This view is being removed. Shut down operations
@@ -59,6 +61,18 @@ export default Component.extend(LoadMore, {
     $(this.element).off("click.discourse-redirect", ".excerpt a");
   }),
 
+  _updateLastDecoratedElement() {
+    const nodes = this.element.querySelectorAll(".user-stream-item");
+    if (nodes.length === 0) {
+      return;
+    }
+    const lastElement = nodes[nodes.length - 1];
+    if (lastElement === this._lastDecoratedElement) {
+      return;
+    }
+    this._lastDecoratedElement = lastElement;
+  },
+
   actions: {
     removeBookmark(userAction) {
       const stream = this.stream;
@@ -123,7 +137,15 @@ export default Component.extend(LoadMore, {
 
       this.set("loading", true);
       const stream = this.stream;
-      stream.findItems().then(() => this.set("loading", false));
+      stream.findItems().then(() => {
+        this.set("loading", false);
+        let element = this._lastDecoratedElement?.nextElementSibling;
+        while (element) {
+          this.trigger("user-stream:new-item-inserted", element);
+          element = element.nextElementSibling;
+        }
+        this._updateLastDecoratedElement();
+      });
     },
   },
 });
diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js
index 7094c0a..b11a07e 100644
--- a/app/assets/javascripts/discourse/app/lib/plugin-api.js
+++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js
@@ -311,12 +311,10 @@ class PluginApi {
     if (!opts.onlyStream) {
       decorate(ComposerEditor, "previewRefreshed", callback, opts.id);
       decorate(DiscourseBanner, "didInsertElement", callback, opts.id);
-      decorate(
-        this.container.factoryFor("component:user-stream").class,
-        "didInsertElement",
-        callback,
-        opts.id
-      );
+      ["didInsertElement", "user-stream:new-item-inserted"].forEach((event) => {
+        const klass = this.container.factoryFor("component:user-stream").class;
+        decorate(klass, event, callback, opts.id);
+      });
     }
   }
 

GitHub sha: 7f3468e7d507c94059f5b686537db50b05032793

This commit appears in #14600 which was approved by eviltrout. It was merged by OsamaSayegh.

This commit has been mentioned on Discourse Meta. There might be relevant details there: