跳过正文
  1. 归档/

Comments Widget的自动主题和双评论系统

·3026 字·
技术 Hugo PaperMod
Xeonzilla
作者
Xeonzilla
“XEON Endorses Open-sourcing Nvidia”
目录

起因
#

在初步完成个人Github Pages的搭建后,我就一直在考察除Giscus1外的其他评论系统。虽说Giscus托管在Github上、而且免费开源、不包含广告,看上去全是优点,但是我忽略了一个重要的因素:用户来源。

正如我在关于中所说,这个博客是我的动画Telegram频道2的一种拓展,主要发布的内容也是动画相关的观后感。至于技术博文或是其他内容,只有在我认为有必要时才起身写作。

而Github Pages和Giscus,还是用在技术博客居多。在技术博客上要求用户使用Github登录,是一件非常自然、非常顺理成章的事;而在一篇动画观后感的评论区要求使用Github登录,显得有些离题万里、不合逻辑,这极有可能打击了用户的评论欲望。

于是,我选择接入Comments Widget3,与Giscus组成博客的双评论系统,以缓解用户来源差异的问题。

代码展示
#

代码大致能分为切换按钮、Giscus和Comments Widget三部分:

<div class="comment-toggle-buttons">
    <button id="show-giscus" class="active" onclick="showGiscus()">Github Discussions</button>
    <button id="show-telegram" onclick="showTelegram()">Telegram Comments</button>
</div>

<div id="github-discussions" class="comment-box"></div>
<div id="telegram-comments" class="comment-box"></div>

<style>
    .comment-toggle-buttons {
        display: flex;
        justify-content: center;
        margin-top: 50px;
        margin-bottom: 10px;
        gap: 10px;
    }

    #telegram-comments {
        min-height: 378.2px;
        display: none;
    }

    .comment-toggle-buttons button {
        display: block;
        padding: 0 14px;
        color: var(--primary);
        font-size: 14px;
        line-height: 34px;
        background: var(--code-bg);
        border-radius: var(--radius);
        border: 1px solid var(--border);
    }

    .comment-toggle-buttons button:hover {
        background: var(--border);
    }

    .comment-toggle-buttons button.active {
        pointer-events: none;
        background-color: transparent;
    }
</style>

<script>
    let telegramLoaded = false;

    function showGiscus() {
        document.getElementById('show-giscus').classList.add('active');
        document.getElementById('show-telegram').classList.remove('active');
        document.getElementById('github-discussions').style.display = 'block';
        document.getElementById('telegram-comments').style.display = 'none';
        setGiscusTheme();
    }

    function showTelegram() {
        document.getElementById('show-giscus').classList.remove('active');
        document.getElementById('show-telegram').classList.add('active');
        document.getElementById('github-discussions').style.display = 'none';
        document.getElementById('telegram-comments').style.display = 'block';
        if (!telegramLoaded) {
            loadTelegramWidget();
            telegramLoaded = true;
        }
    }

    const getStoredTheme = () => localStorage.getItem("pref-theme") === "dark" ? "dark" : "light";

    const setGiscusTheme = () => {
        const sendMessage = (message) => {
            const iframe = document.querySelector('iframe.giscus-frame');
            if (iframe) {
                iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
            }
        };
        sendMessage({ setConfig: { theme: getStoredTheme() === 'dark' ? 'noborder_dark' : 'noborder_light' } });
    };

    const loadTelegramWidget = () => {
        const telegramDiv = document.getElementById('telegram-comments');
        telegramDiv.innerHTML = "";
        const telegramScript = document.createElement("script");
        telegramScript.src = "https://comments.app/js/widget.js?3";
        telegramScript.defer = true;
        telegramScript.setAttribute("data-comments-app-website", "i7gjmkOw");
        telegramScript.setAttribute("data-limit", "100");
        telegramScript.setAttribute("data-height", "371");
        telegramScript.setAttribute("data-dislikes", "1");
        telegramScript.setAttribute("data-colorful", "1");
        telegramScript.setAttribute("data-dark", getStoredTheme() === "dark" ? "1" : "0");
        telegramDiv.appendChild(telegramScript);
    };

    const setTelegramTheme = () => {
        loadTelegramWidget();
    };

    document.addEventListener("DOMContentLoaded", () => {
        const giscusAttributes = {
            "src": "https://giscus.app/client.js",
            "data-repo": "Xeonzilla/Xeonzilla.github.io",
            "data-repo-id": "R_kgDOL6BvNQ",
            "data-category": "Announcements",
            "data-category-id": "DIC_kwDOL6BvNc4Cfb8m",
            "data-mapping": "pathname",
            "data-strict": "1",
            "data-reactions-enabled": "1",
            "data-emit-metadata": "0",
            "data-input-position": "top",
            "data-theme": getStoredTheme() === 'dark' ? 'noborder_dark' : 'noborder_light',
            "data-lang": "zh-CN",
            "crossorigin": "anonymous",
        };

        const giscusScript = document.createElement("script");
        Object.entries(giscusAttributes).forEach(
            ([key, value]) => giscusScript.setAttribute(key, value));
        giscusScript.defer = true;
        document.querySelector("#github-discussions").appendChild(giscusScript);

        if (document.getElementById('telegram-comments').style.display === 'block') {
            loadTelegramWidget();
            telegramLoaded = true;
        }

        const themeSwitcher = document.querySelector("#theme-toggle");
        if (themeSwitcher) {
            themeSwitcher.addEventListener("click", () => {
                setGiscusTheme();
                setTelegramTheme();
            });
        }
    });
</script>

其中,Giscus的自动主题切换有现成的方案,具体可以参考

我的代码在他们的基础上小修小改,就不在此拾人牙慧了。

下面的代码是从完整代码中分离出来的,它们极有可能无法独立工作,仅作解析思路用。

Comments Widget
#

官方页面会在你填入所需信息和所选项后自动生成代码,和Giscus类似,如无特殊需求,直接复制代码至layouts/partical/comments.html即可使用。

但是为了实现功能,我们需要对代码进行改造。

<div id="telegram-comments" class="comment-box"></div>

<style>
    #telegram-comments {
        min-height: 378.2px;
        display: none;
    }
</style>

<script>
    let telegramLoaded = false;

    const getStoredTheme = () => localStorage.getItem("pref-theme") === "dark" ? "dark" : "light";

    const loadTelegramWidget = () => {
        const telegramDiv = document.getElementById('telegram-comments');
        telegramDiv.innerHTML = "";
        const telegramScript = document.createElement("script");
        telegramScript.src = "https://comments.app/js/widget.js?3";
        telegramScript.defer = true;
        telegramScript.setAttribute("data-comments-app-website", "i7gjmkOw");
        telegramScript.setAttribute("data-limit", "100");
        telegramScript.setAttribute("data-height", "371");
        telegramScript.setAttribute("data-dislikes", "1");
        telegramScript.setAttribute("data-colorful", "1");
        telegramScript.setAttribute("data-dark", getStoredTheme() === "dark" ? "1" : "0");
        telegramDiv.appendChild(telegramScript);
    };

    const setTelegramTheme = () => {
        loadTelegramWidget();
    };

    document.addEventListener("DOMContentLoaded", () => {
        if (document.getElementById('telegram-comments').style.display === 'block') {
            loadTelegramWidget();
            telegramLoaded = true;
        }
    });
</script>

代码最核心的部分其实就是一个loadTelegramWidget(),以加载Comments Widget,其他的代码都依赖于此。

我们需要监听页面主题的切换按钮,当页面主题切换时,触发setTelegramTheme()的操作。虽说这个行为的目的是主题设置,但是实际上是重新加载Comments Widget,从代码上也可以看出来。

为什么是重载而不是直接切换主题呢?因为我似乎没有找到通过通信使Comments Widget切换主题的方法,Telegram也没有提供相关文档。我曾尝试将Giscus的代码逻辑套用到Comments Widget上,但是并不生效。以重载完成主题切换,是一种无奈之举。

等我找到切换主题的方法,或是Telegram提供了相关文档,又或是有人给出了教程,这套方案就会被弃用。不管我如何完善和优化重载的逻辑,它在性能消耗和可靠性上还是不如原生的主题切换。

重载能完成主题切换,但是我们不需要让它在任何操作后都重载,所以需要设置一个标志let telegramLoaded = false,当Comments Widget完成首次加载后将标志设置为true,避免用户多次切换评论区导致重复加载。

在样式上,min-height: 378.2px起占位作用,在Comments Widget加载前维持页面元素的相对位置。378.2px是Giscus没有评论时的高度,也是所有情况下的最小高度,保持默认高度一致,使用户在切换评论系统时,页面元素不会位移。

切换按钮
#

相比之下,切换按钮的逻辑就较为简单,主要是样式上的调整。

<div class="comment-toggle-buttons">
    <button id="show-giscus" class="active" onclick="showGiscus()">Github Discussions</button>
    <button id="show-telegram" onclick="showTelegram()">Telegram Comments</button>
</div>

<style>
    .comment-toggle-buttons {
        display: flex;
        justify-content: center;
        margin-top: 50px;
        margin-bottom: 10px;
        gap: 10px;
    }

    .comment-toggle-buttons button {
        display: block;
        padding: 0 14px;
        color: var(--primary);
        font-size: 14px;
        line-height: 34px;
        background: var(--code-bg);
        border-radius: var(--radius);
        border: 1px solid var(--border);
    }

    .comment-toggle-buttons button:hover {
        background: var(--border);
    }

    .comment-toggle-buttons button.active {
        pointer-events: none;
        background-color: transparent;
    }
</style>

<script>
    let telegramLoaded = false;

    function showGiscus() {
        document.getElementById('show-giscus').classList.add('active');
        document.getElementById('show-telegram').classList.remove('active');
        document.getElementById('github-discussions').style.display = 'block';
        document.getElementById('telegram-comments').style.display = 'none';
        setGiscusTheme();
    }

    function showTelegram() {
        document.getElementById('show-giscus').classList.remove('active');
        document.getElementById('show-telegram').classList.add('active');
        document.getElementById('github-discussions').style.display = 'none';
        document.getElementById('telegram-comments').style.display = 'block';
        if (!telegramLoaded) {
            loadTelegramWidget();
            telegramLoaded = true;
        }
    }

    const themeSwitcher = document.querySelector("#theme-toggle");
    if (themeSwitcher) {
        themeSwitcher.addEventListener("click", () => {
            setGiscusTheme();
            setTelegramTheme();
        });
    }
</script>

大部分的样式都遵循主题的设定,使按钮与主题看上去更和谐统一。

通过class="active"display: blockdisplay: none间的切换、隐藏与显示,我们得以用<button>控制两个评论区的显示。

pointer-events: none应用在当前激活的评论区按钮上,能够使用户无法再次选中,杜绝了很多奇怪bug触发的可能性。

为什么不是Discussion Widget
#

Telegram除了提供Comments Widget外,还提供了Discussion Widget4将频道的评论区接入网页,同样可以作为评论系统,但是它存在着挺多问题:

  • 博客评论区与频道评论区完全关联
  • Telegram无法识别经编辑的链接

完全关联这一点很好理解,毕竟Discussion Widget就是起到一种“桥接”的作用,将博客与频道绑定起来。如果我发布了技术博文,但我不想将链接广播到我的动画频道,这时的Discussion Widget就无法起到评论系统的作用。

第二点问题不知是Telegram设计上的问题还是特性,当第一次发送链接时,Telegram能够主动识别;而如果是一条经过编辑的消息,即使它是链接,Telegram也不会识别。

这样的特性不仅为Discussion Widget带来不便,还在广告投放者手中“被妙用”:他们往往会先在群组中发送一条正常的消息,随后将消息编辑为广告文案,以此躲过反广告机器人的识别与检测。

总体而言,Discussion Widget并不是一套完善、高度可用的评论系统,它更适用于日常社交与个人Telegram频道相关性强的用户,否则,Discussion Widget并没有与Waline一较高下的实力。

如果像我一样已经有一个公开频道,此时接入Discussion Widget将会非常不便,需要为旧博文手动关联评论区,影响频道的内容展示;而如果没有公开频道,创建并使用Discussion Widget也未尝不可。

或许是Waline
#

在第二个评论系统的选择上,我也有短暂考虑过Waline5,它被广泛使用、口碑良好且没有广告,给我的第一印象不错。

唯一的问题就是,它需要部署。虽说Waline也可以免费部署到Vercel6,但是我个人还是倾向于使用一些现成的服务,例如Giscus的使用Github Discussion,Comments Widget的接入Telegram。

使用额外部署的评论系统、调用一套第三方服务,就像引入新的外部依赖,它不一定会让博客变得不稳定,但是会让我感到复杂、不安定。相比之下,使用Comments Widget,将评论系统接入日常使用的社交软件,给我的感觉更合理、更无缝。

但是结论不能没有余地,当有需要再次添加评论系统,或是博客重构的时候,Waline(或是它的继任者)会是一个好选择。

相关文章

更换博客主题:从PaperMod到Blowfish
·2438 字
技术 Hugo PaperMod Blowfish
Hugo中的AVIF与累积布局偏移(CLS)
··5877 字
技术 Hugo PaperMod
更换博客评论系统:Waline
·1210 字
技术 Hugo Blowfish