Web3 博客评论区的最终方案

Jan 1, 2025 at 14:33:11

就像短博文里说的那样,使用 giscus 配合 Planet 批量设置评论区还是有一点麻烦的。

其实主要问题是用户可能通过不同的 IPFS 网关访问,这就导致了不管是 URL 还是 pathname 都不可靠。帖子的标题确实可行,但可能出现没有标题的短博文这种情况。

一开始的选择是如果没有标题那么就用 JS 把 og:title 写入成发帖时间,可以实现功能,但会有两个问题,如果后期加上了标题、或者发帖时间被修改(Planet 似乎有一个 bug 是编辑帖子的时候时间有概率会变成现在),而手动设置时间则无法设置“秒”,出现这两种情况的话,之前的评论就不会显示了。

使用标题还有另一个需要处理的问题就是标题本身发生变化的情况。

所以最终还是决定用 JS 将所有文章的 Open Graph Title 全部动态替换成 Post ID(反正读取这玩意的服务都不会去跑 JS),最终代码如下,似乎只能放在 header 的 Custom Code 里才能生效:

适用于 Plain 模板:

<script>
(function() {
  // --- Step 1: Compare og:site_name to the actual page title
  const siteNameTag = document.querySelector('meta[property="og:site_name"]');
  const siteName = siteNameTag ? siteNameTag.getAttribute('content').trim() : '';
  const pageTitle = document.title.trim();

  // --- Step 2: If they differ, update og:title with the last part of the pathname
  if (siteName !== pageTitle) {
    let path = window.location.pathname;
    // Remove trailing slash if any
    if (path.endsWith('/')) {
      path = path.slice(0, -1);
    }
    // Extract the last segment of the path
    const lastPart = path.substring(path.lastIndexOf('/') + 1);

    // Update <meta property="og:title" ...>
    const ogTitleTag = document.querySelector('meta[property="og:title"]');
    if (ogTitleTag) {
      ogTitleTag.setAttribute('content', lastPart);
      console.log(`Updated og:title to "${lastPart}"`);
    }
  }

  // --- Step 3: Create the wrapper <div> elements
  const outerDiv = document.createElement('div');
  outerDiv.style.display = 'flex';
  outerDiv.style.alignItems = 'center';
  outerDiv.style.padding = '0px 20px';

  const innerDiv = document.createElement('div');
  innerDiv.style.flex = '1';
  innerDiv.style.width = '100%';
  innerDiv.style.maxWidth = '700px';
  innerDiv.style.margin = '20px auto';

  // --- Step 4: Load the Giscus script
  const giscusScript = document.createElement('script');
  giscusScript.src = "https://giscus.app/client.js";
  giscusScript.setAttribute('data-repo', "extrawdw-code/blog-giscus");
  giscusScript.setAttribute('data-repo-id', "R_kgDONkfVTg");
  giscusScript.setAttribute('data-category', "General");
  giscusScript.setAttribute('data-category-id', "DIC_kwDONkfVTs4ClpWU");
  giscusScript.setAttribute('data-mapping', "og:title");
  giscusScript.setAttribute('data-strict', "1");
  giscusScript.setAttribute('data-reactions-enabled', "1");
  giscusScript.setAttribute('data-emit-metadata', "0");
  giscusScript.setAttribute('data-input-position', "top");
  giscusScript.setAttribute('data-theme', "preferred_color_scheme");
  giscusScript.setAttribute('data-lang', "en");
  giscusScript.setAttribute('data-loading', "lazy");
  giscusScript.setAttribute('crossorigin', "anonymous");
  giscusScript.async = true;

  // Append script inside the inner div
  innerDiv.appendChild(giscusScript);

  // Then append the inner div to the outer div
  outerDiv.appendChild(innerDiv);

  // Insert the wrapper into <head> or near end of <body>
  document.addEventListener('DOMContentLoaded', function() {
    document.body.appendChild(outerDiv);
  });
})();
</script>

适用于 Sepia 模板:

<script>
(function() {
  // --- Step 1: Compare og:site_name to the actual page title
  const siteNameTag = document.querySelector('meta[property="og:site_name"]');
  const siteName = siteNameTag ? siteNameTag.getAttribute('content').trim() : '';
  const pageTitle = document.title.trim();

  // --- Step 2: If they differ, update og:title with the last part of the pathname
  if (siteName !== pageTitle) {
    let path = window.location.pathname;
    // Remove trailing slash if any
    if (path.endsWith('/')) {
      path = path.slice(0, -1);
    }
    // Extract the last segment of the path
    const lastPart = path.substring(path.lastIndexOf('/') + 1);

    // Update <meta property="og:title" ...>
    const ogTitleTag = document.querySelector('meta[property="og:title"]');
    if (ogTitleTag) {
      ogTitleTag.setAttribute('content', lastPart);
      console.log(`Updated og:title to "${lastPart}"`);
    }
  }

  // --- Step 3: Create the wrapper <div> elements
  const outerDiv = document.createElement('div');
  outerDiv.style.display = 'flex';
  outerDiv.style.alignItems = 'center';
  outerDiv.style.padding = '0px 20px';

  const innerDiv = document.createElement('div');
  innerDiv.style.flex = '1';
  innerDiv.style.width = '100%';
  innerDiv.style.maxWidth = '700px';
  innerDiv.style.margin = '20px auto';

  // --- Step 4: Load the Giscus script
  const giscusScript = document.createElement('script');
  giscusScript.src = "https://giscus.app/client.js";
  giscusScript.setAttribute('data-repo', "extrawdw-code/blog-giscus");
  giscusScript.setAttribute('data-repo-id', "R_kgDONkfVTg");
  giscusScript.setAttribute('data-category', "General");
  giscusScript.setAttribute('data-category-id', "DIC_kwDONkfVTs4ClpWU");
  giscusScript.setAttribute('data-mapping', "og:title");
  giscusScript.setAttribute('data-strict', "1");
  giscusScript.setAttribute('data-reactions-enabled', "1");
  giscusScript.setAttribute('data-emit-metadata', "0");
  giscusScript.setAttribute('data-input-position', "top");
  giscusScript.setAttribute('data-theme', "preferred_color_scheme");
  giscusScript.setAttribute('data-lang', "en");
  giscusScript.setAttribute('data-loading', "lazy");
  giscusScript.setAttribute('crossorigin', "anonymous");
  giscusScript.async = true;

  // Append script inside the inner div
  innerDiv.appendChild(giscusScript);

  // Then append the inner div to the outer div
  outerDiv.appendChild(innerDiv);

  // Finally, place this wrapper inside the div with id="main-container"
  document.addEventListener('DOMContentLoaded', function() {
    const container = document.getElementById("main-container");
    if (container) {
      container.appendChild(outerDiv);
    } else {
      console.warn('Could not find a container with id="main-container".');
    }
  });
})();
</script>

外边套的俩 div 是参考的站长的网站设计。

最终效果如下:

Screenshot 2025-01-01 at 14.46.06 Screenshot 2025-01-01 at 14.45.24

在聚合站点的时候,只要打开“重用原始ID”的选项就可以让评论也“同步”过去。

p.s.:Planet 在聚合站点且保留 ID 的情况下,Bug 太多了……经常出现原帖被当成聚合过来的帖子,然后无法编辑需要重启的情况。