如何在“post_1”后添加自定义 HTML

帖子是 widgets,这意味着你试图实现的功能不仅仅是添加 HTML,还需要做更多的工作。

Discourse 主题具备 decorate widgets 的能力,你可以利用这一点。

装饰 widget 的方法在上面的链接中已有说明,让我们专注于你试图实现的目标——在每个主题的第一个帖子后添加标记。

首先,将标记添加到所有帖子中。例如:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  return helper.h("div", "test text");
});
</script>

将其添加到你的主题的头部区域。这样就足以在每个帖子下方添加“test text”了。

让我们分解一下上面的脚本:

api.decorateWidget("post:after", helper => {

调用 decorateWidget 方法,目标 widget 是 post,目标位置是 after。即在帖子 widget 之后。

helper 是一个内置辅助对象,它提供了对许多功能的访问权限,稍后我会详细解释。

return helper.h("div", "test text")

这是你想要添加的额外标记。你可能会注意到其中没有 HTML,这是因为 Discourse widget 输出的是虚拟节点(virtual nodes),而不是原始 HTML。

解释什么是虚拟节点或语法如何工作超出了本主题的范围,因此我将跳过这部分。我计划写一篇关于如何编写虚拟节点的教程,但目前这里有一些示例:

helper.h("div", "test text")

渲染为:

<div>test text</div>

而:

return helper.h("div#custom-ad", [
  helper.h(
    "a.custom-ad-link",
    { href: "example.com" },
    helper.h("img", { src: "https://picsum.photos/id/74/750/90" })
  )
]);

将渲染为:

<div id="custom-ad">
  <a href="example.com" class="custom-ad-link">
    <img src="https://picsum.photos/id/74/750/90">
  </a>
</div>

简而言之,一个节点的结构如下:

helper.h(selector, {properties}, children)

我会在虚拟节点教程中进一步解释这一点。

现在你已经准备好了节点,只需要将整个脚本添加到主题的头部区域,例如:

<script type="text/discourse-plugin" version="0.8">
  api.decorateWidget("post:after", helper => {
    return helper.h("div#custom-ad", [
      helper.h(
        "a.custom-ad-link",
        { href: "example.com" },
        helper.h("img", { src: "https://picsum.photos/id/74/750/90" })
      )
    ]);
  });
</script>

然而,这里还有一个问题:广告会被插入到流中的每个帖子下方,这并不是理想的效果。

这时 helper 就派上用场了。帖子属性会传递给 helper,因此你可以快速执行:

console.log(helper)

你将看到所有可用的帖子属性。

这些只是示例,其中还有更多内容。

幸运的是,firstPost 属性可供我们使用,因此你只需要将其放入一个条件判断中,仅在确实是第一个帖子时渲染广告标记,否则不执行任何操作。例如:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  const firstPost = helper.attrs.firstPost;
  const h = helper.h;
  if (firstPost) {
    return h("div#custom-ad", [
      h(
        "a.custom-ad-link",
        { href: "example.com" },
        h("img", { src: "https://picsum.photos/id/74/750/90" })
      )
    ]);
  }
});
</script>

这样,广告将仅在第一个帖子后插入。还需要做的一件事是为图片添加高度,否则在加载时会导致抖动。正如我上面简要提到的,height 属性是图片标签的一个属性,因此你需要将其与 src 一起添加。

将所有内容整合后,以下是你试图实现功能的最终代码:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  const firstPost = helper.attrs.firstPost;
  const h = helper.h;
  if (firstPost) {
    return h("div#custom-ad", [
      h(
        "a.custom-ad-link",
        { href: "example.com" },
        h("img", { src: "https://picsum.photos/id/74/750/90", height: "90" })
      )
    ]);
  }
});
</script>

最后需要强调的是,如果虚拟节点过于复杂,你实际上也可以使用原始 HTML,但不推荐这样做,使用虚拟节点要好得多。使用原始 HTML 的相同脚本如下:

<script type="text/discourse-plugin" version="0.8">
  const RawHtml = require("discourse/widgets/raw-html").default;
  api.decorateWidget("post:after", helper => {
    const firstPost = helper.attrs.firstPost;
    if (firstPost) {
      return [
        new RawHtml({
          html: `<div id="custom-ad">
                   <a href="example.com">
                     <img src="https://picsum.photos/id/74/750/90" height="90">
                   </a>
                 </div>`
        })
      ];
    }
  });
</script>

但再次强调,这不推荐。