为特定帖子更换头像和用户名

问题:我希望用户能够以 RPG 角色的身份发帖。我希望他们能够将模板插入帖子中——类似于以下内容:

[wrap="characterpost"]
[characterav]https://image.link.example.png[/characterav]
[charactername][[Character Name]][/charactername]
[/wrap]

然后使用脚本将图片替换为原始头像,并将 charactername 主题链接放在用户名之前。例如,如果帖子通常显示“Username”,我希望将其替换为“Character Name played by Username”。
(“Character Name”将包含一个指向角色表的主题的链接,希望仅使用 wikilinks 主题组件以方便使用。)

我将帖子骨架粘贴到了 Codepen 中,并编写了一些可以做到这一点的 JavaScript。但是,在将其添加到帖子装饰器并使用 API 实现实时工作时,我遇到了障碍。

这是我目前在 common>header 中拥有的:

<script type="text/discourse-plugin" version="0.8">
api.decorateCookedElement(
  element => {
    
    // 在帖子中查找 characterpost 标签
    const characterPost = element.querySelector('[data-wrap="characterpost"]');
	
    // 查找 characterpost 标签的父级,其中包含头像和用户名
    const characterPostParent = characterPost.closest('article');
	
    // 将其设为红色以查看是否有效
    characterPostParent.style.backgroundColor = "red";
    
  },
  {
    id: 'render-character-post', onlyStream: true, afterAdopt: true
  }
);
</script>

这会引发错误。是否可以使用 decorateCookedElement 访问帖子的“article”包装器,以便我可以访问用户名和头像?如果不行,我该如何进行?

4 个赞

你好,unfairest,

我偶然发现了这个帖子,我想我可以试试!

这是第一个版本;希望代码不会太糟糕。 :slightly_smiling_face:
我没有广泛测试所有边缘情况。这是一个起点。

  • 预期的 wrap 格式: [wrap=character avatar=\"<URL>\" name=\"<Name>\"][/wrap]
  • 确保 [wrap] 下方有一个空行
  • 您可以使用 CSS 自定义角色名称(请参阅 charactercharacter-extra 类)

如果您有任何问题,请告诉我 :slight_smile:

头部
<script type="text/discourse-plugin" version="0.8.13">

let characters = new Map();

function searchTag(obj, tag) {
  if (!obj || !obj.firstObject) return;
  
  const firstObj = obj.firstObject;
  return firstObj.tagName === tag ? firstObj : searchTag(firstObj.children, tag);
}
            
api.addPostTransformCallback(post => 
{
    if (post.post_number <= 1 || post.post_type !== 1) {
        return;
    }

    const matches = post.cooked.match(/data-wrap="character"\s+data-avatar="(?<avatar>[^"]+)"\s+data-name="(?<name>[^"]+)"/i);
    
    if (!matches) {
        characters.delete(post.id);
        return;
    }
    
    // TODO: sanity check for avatar/name
    
    const {name, avatar} = matches.groups;
    
    characters.set(post.id, {name, avatar});
    
});

api.reopenWidget("post-avatar", {
    html(attrs) {
        const html = this._super(attrs);
        
        if (attrs.id && characters.has(attrs.id)) {
            const imageHtml = searchTag(html, 'IMG');
    
            if (imageHtml) {
                imageHtml.properties.attributes.src = characters.get(attrs.id).avatar;
            }
        }
        
        return html;
    }
});

api.reopenWidget("poster-name", {
    html(attrs) {
        let html = this._super(attrs);
        
        if (attrs.id && characters.has(attrs.id)) {
            const h = require("virtual-dom").h;
            
            html = [
                h('span.character', this.userLink(attrs, characters.get(attrs.id).name)),
                h('span.character-extra', 'played by'), // optional
                ...html
            ];
        }
        
        return html;
    }
});

</script>
CSS
.names {
    .character a {
        font-weight: bold;
        color: var(--tertiary-high);
    }
    
    .character-extra {
        
    }
}

.cooked, .d-editor-preview {
    p:has(> [data-wrap="character"]) {
        display: none;
    }
    
    p:has(> [data-wrap="character"]) + * {
        margin-top: 0;
    }
}

这是它外观的小演示:

7 个赞

这太酷了,谢谢分享!我也在找类似的东西。您知道这是否也会影响帖子在搜索中的显示方式吗?

好问题!标签名称及其内容是帖子内容的一部分;这可能会影响搜索结果。
我将尝试更简洁的方法,例如使用模态框,然后将其保存在自定义字段中。

编辑:我想我误解了 :smile: 如果您谈论的是搜索结果中出现更改,答案是否定的(HTML也不会出现)。

2 个赞

这很酷。你的视频滚动得很快。网址是在论坛网站上吗?

嘿,丹。你是什么意思?

头像网址。是仅上传的图片还是指向外部网站?

这是一个外部 URL。应该可以处理本地 URL。

2 个赞

好的,谢谢

1 个赞

也许可以考虑在社区维基上提议一个论坛模板指南。因为人们经常来寻找不同类型论坛设置的灵感。

在这种情况下,可能有点小众。但可以有一个桌面角色扮演游戏模板,其中包含您创建的这个功能。