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 https://github.com/discourse/discourse-follow/pull/37)

Co-authored-by: Robin Ward <robin.ward@gmail.com>
This commit is contained in:
Osama Sayegh 2021-10-14 08:10:13 +03:00 committed by GitHub
parent 82945a3d21
commit 7f3468e7d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 7 deletions

View File

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

View File

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