mirror of
https://github.com/discourse/discourse.git
synced 2025-04-27 03:04:29 +08:00
DEV: Refactor PostList cooked HTML rendering (#31307)
- Remove JQuery - Move decoration into `components/post-item`, instead of managing it from the top-level user-stream component - Use new `DecoratedHtml` component
This commit is contained in:
parent
b471e3d5ba
commit
35084d3089
@ -2,18 +2,22 @@ import Component from "@glimmer/component";
|
|||||||
import { fn } from "@ember/helper";
|
import { fn } from "@ember/helper";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { or } from "truth-helpers";
|
||||||
import DButton from "discourse/components/d-button";
|
import DButton from "discourse/components/d-button";
|
||||||
|
import DecoratedHtml from "discourse/components/decorated-html";
|
||||||
import ExpandPost from "discourse/components/expand-post";
|
import ExpandPost from "discourse/components/expand-post";
|
||||||
import PostListItemDetails from "discourse/components/post-list/item/details";
|
import PostListItemDetails from "discourse/components/post-list/item/details";
|
||||||
import avatar from "discourse/helpers/avatar";
|
import avatar from "discourse/helpers/avatar";
|
||||||
import concatClass from "discourse/helpers/concat-class";
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
import icon from "discourse/helpers/d-icon";
|
import icon from "discourse/helpers/d-icon";
|
||||||
|
import { bind } from "discourse/lib/decorators";
|
||||||
import { userPath } from "discourse/lib/url";
|
import { userPath } from "discourse/lib/url";
|
||||||
|
|
||||||
export default class PostListItem extends Component {
|
export default class PostListItem extends Component {
|
||||||
@service site;
|
@service site;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
@service appEvents;
|
||||||
|
|
||||||
get moderatorActionClass() {
|
get moderatorActionClass() {
|
||||||
return this.args.post.post_type === this.site.post_types.moderator_action
|
return this.args.post.post_type === this.site.post_types.moderator_action
|
||||||
@ -70,6 +74,15 @@ export default class PostListItem extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
decoratePostContent(element, helper) {
|
||||||
|
this.appEvents.trigger(
|
||||||
|
"decorate-non-stream-cooked-element",
|
||||||
|
element,
|
||||||
|
helper
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="post-list-item
|
class="post-list-item
|
||||||
@ -147,11 +160,11 @@ export default class PostListItem extends Component {
|
|||||||
data-user-id={{@post.user_id}}
|
data-user-id={{@post.user_id}}
|
||||||
class="excerpt"
|
class="excerpt"
|
||||||
>
|
>
|
||||||
{{#if @post.expandedExcerpt}}
|
<DecoratedHtml
|
||||||
{{~htmlSafe @post.expandedExcerpt~}}
|
@html={{htmlSafe (or @post.expandedExcerpt @post.excerpt)}}
|
||||||
{{else}}
|
@decorate={{this.decoratePostContent}}
|
||||||
{{~htmlSafe @post.excerpt~}}
|
@className="cooked"
|
||||||
{{/if}}
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{yield to="belowPostItem"}}
|
{{yield to="belowPostItem"}}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { hash } from "@ember/helper";
|
import { hash } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { getOwner } from "@ember/owner";
|
import { getOwner } from "@ember/owner";
|
||||||
import { later } from "@ember/runloop";
|
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { modifier } from "ember-modifier";
|
|
||||||
import $ from "jquery";
|
|
||||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||||
import PostActionDescription from "discourse/components/post-action-description";
|
import PostActionDescription from "discourse/components/post-action-description";
|
||||||
import PostList from "discourse/components/post-list";
|
import PostList from "discourse/components/post-list";
|
||||||
@ -26,28 +23,6 @@ export default class UserStreamComponent extends Component {
|
|||||||
@service appEvents;
|
@service appEvents;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@service router;
|
@service router;
|
||||||
@tracked lastDecoratedElement;
|
|
||||||
|
|
||||||
eventListeners = modifier((element) => {
|
|
||||||
$(element).on("click.details-disabled", "details.disabled", () => false);
|
|
||||||
$(element).on("click.discourse-redirect", ".excerpt a", (e) => {
|
|
||||||
return ClickTrack.trackClick(e, getOwner(this));
|
|
||||||
});
|
|
||||||
later(() => {
|
|
||||||
this.updateLastDecoratedElement();
|
|
||||||
this.appEvents.trigger("decorate-non-stream-cooked-element", element);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
$(element).off("click.details-disabled", "details.disabled");
|
|
||||||
// Unbind link tracking
|
|
||||||
$(element).off("click.discourse-redirect", ".excerpt a");
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
get filterClassName() {
|
get filterClassName() {
|
||||||
const filter = this.args.stream?.filter;
|
const filter = this.args.stream?.filter;
|
||||||
@ -68,20 +43,6 @@ export default class UserStreamComponent extends Component {
|
|||||||
return "username";
|
return "username";
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
updateLastDecoratedElement() {
|
|
||||||
const nodes = document.querySelectorAll(".user-stream-item");
|
|
||||||
if (!nodes || nodes.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastElement = nodes[nodes.length - 1];
|
|
||||||
if (lastElement === this.lastDecoratedElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.lastDecoratedElement = lastElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async removeBookmark(userAction) {
|
async removeBookmark(userAction) {
|
||||||
try {
|
try {
|
||||||
@ -140,19 +101,21 @@ export default class UserStreamComponent extends Component {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
later(() => {
|
|
||||||
let element = this.lastDecoratedElement?.nextElementSibling;
|
|
||||||
while (element) {
|
|
||||||
this.appEvents.trigger("user-stream:new-item-inserted", element);
|
|
||||||
this.appEvents.trigger("decorate-non-stream-cooked-element", element);
|
|
||||||
element = element.nextElementSibling;
|
|
||||||
}
|
|
||||||
this.updateLastDecoratedElement();
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.args.stream.content;
|
return this.args.stream.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handleClick(event) {
|
||||||
|
if (event.target.matches("details.disabled")) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.matches(".excerpt a")) {
|
||||||
|
return ClickTrack.trackClick(event, getOwner(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PostList
|
<PostList
|
||||||
@posts={{@stream.content}}
|
@posts={{@stream.content}}
|
||||||
@ -166,7 +129,7 @@ export default class UserStreamComponent extends Component {
|
|||||||
@resumeDraft={{this.resumeDraft}}
|
@resumeDraft={{this.resumeDraft}}
|
||||||
@removeDraft={{this.removeDraft}}
|
@removeDraft={{this.removeDraft}}
|
||||||
class={{concatClass "user-stream" this.filterClassName}}
|
class={{concatClass "user-stream" this.filterClassName}}
|
||||||
{{this.eventListeners @stream}}
|
{{on "click" this.handleClick}}
|
||||||
>
|
>
|
||||||
<:abovePostItemHeader as |post|>
|
<:abovePostItemHeader as |post|>
|
||||||
<PluginOutlet
|
<PluginOutlet
|
||||||
|
@ -165,6 +165,8 @@ export default class Post extends RestModel {
|
|||||||
@trackedPostProperty user_deleted;
|
@trackedPostProperty user_deleted;
|
||||||
@trackedPostProperty user_id;
|
@trackedPostProperty user_id;
|
||||||
@trackedPostProperty yours;
|
@trackedPostProperty yours;
|
||||||
|
@trackedPostProperty expandedExcerpt;
|
||||||
|
@trackedPostProperty excerpt;
|
||||||
|
|
||||||
customShare = null;
|
customShare = null;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ acceptance("User Drafts", function (needs) {
|
|||||||
assert.dom(".user-stream-item").exists("has drafts");
|
assert.dom(".user-stream-item").exists("has drafts");
|
||||||
assert.dom(".user-stream-item:nth-child(3) .category").hasText("meta");
|
assert.dom(".user-stream-item:nth-child(3) .category").hasText("meta");
|
||||||
assert
|
assert
|
||||||
.dom(".user-stream-item:nth-child(3) .excerpt")
|
.dom(".user-stream-item:nth-child(3) .excerpt .cooked")
|
||||||
.hasHtml(
|
.hasHtml(
|
||||||
`here goes a reply to a PM <img src="/images/emoji/twitter/slight_smile.png?v=${IMAGE_VERSION}" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20" style="aspect-ratio: 20 / 20;">`,
|
`here goes a reply to a PM <img src="/images/emoji/twitter/slight_smile.png?v=${IMAGE_VERSION}" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20" style="aspect-ratio: 20 / 20;">`,
|
||||||
"shows the excerpt"
|
"shows the excerpt"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user