帖子是 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>
但再次强调,这不推荐。