Xây dựng phần comment đậm chất dev với github và giscus cho blog

Mấy nay mình quay lại viết blog, thế là phát hiện ra cái blog build bằng Gatsby của mình cũng đã được hơn 3 năm tuổi, có nhiều thứ cũ kĩ quá thì chúng ta cũng nên update, và blog cũng vậy.

Gatsby là một static site generator (SSG) rất nổi tiếng từ hồi 3-4 năm trước, với hơn 50k stars trên github, điểm mạnh là nhanh gọn, và có thể host free trên github page hoặc netlify nên mình đã quyết định chọn em nó.

Blog của mình

Mình thấy việc học bằng cách dạy lại cho người khác, cũng như tổng hợp lại kiến thức của mình để chia sẻ rất hiệu quả, cho nên mình quyết định làm video và viết blog.

Mình đã xây dựng blog này thế nào?

Vào năm 2019, kiến thức về React của mình hầu như bằng 0, cho nên mình lấy luôn một starter theme và chỉnh sửa đôi chút, sau đó chỉ focus vào việc viết bài mà thôi.

Nhưng rồi sau đó khi lượng bài viết càng nhiều, thì vấn đề nó lại tòi ra.

Một vấn đề mình fix được chính là tối ưu hóa lại quá trình tạo bài mới bằng việc tạo 1 CLI tool.

Để mỗi lần tạo bài viết cho gatsby thì không cần phải lặp lại quá trình này:

  1. Vào folder content tạo 1 folder mới với slash gạch ngang dạng “bai-viet-moi”
  2. Mở folder “bai-viet-moi” để tạo file index.md
  3. Mở file markdown vừa tạo, thêm phần frontmatter cho nó như tiêu đề, path, ngày tạo bla blo…

Nói chung là lằng nhằng!

Mình viết 1 command tool với nodejs, chỉ cần 2 dòng gõ tiêu đề và tag, CLI tự động tạo luôn các phần còn lại:

Gatsby CLI để tạo bài viết mới

Gatsby CLI để tạo bài viết mới

Package cli của mình ở đây: https://www.npmjs.com/package/gatsby-tool-cli

Tham khảo: Cách tạo 1 blog bằng gatsbyjs và deploy lên github

Thế blog của mình có vấn đề gì?

Well, hôm nay quay trở lại viết blog thì mình mới nhận ra là blog mình không có hệ thống comment. Trước đây mình lười nên bỏ ngỏ, hôm nay rảnh chút nên tập trung làm nốt.

Thực ra nó còn thiếu cả phần navigation previous và next post, mình có thêm luôn, tuy nhiên bài này mình focus vào phần comment thôi ha.

Nhúng comment vào blog

Về phần comment thì chúng ta có nhiều lựa chọn, tuy nhiên 2 anh lớn nhất chính là Facebook và Discus đều được mình loại trừ.

FB thì mình không thích, hoặc có thể nhiều anh em không thích.

Disqus thì load nặng, và cũng bị vấn đề gì mà đôi lúc mình không vào được, may be do VN mình chặn hoặc do nó chặn VN mình.

Vậy ứng cử viên số 1 phải là Github discussion rồi.

Github discussion

Anh em đọc blog của mình chủ yếu là dev, mà dev thì ai mà chẳng có github.

Thế là quá hợp lý!

Để sử dụng Github discussion làm hệ thống comment thì chúng ta phải sử dụng app thứ 3.

Chúng ta có 2 ứng cử viên cho vụ này là UtterancesGiscus

Mình tham khảo và bỏ qua Utterances, bởi vì 2 nhược điểm:

  • Không có post reaction như Giscus
  • Không scope lại phần reply comment, mà các comment đều bị flatten

Okay, bắt đầu vào làm thôi

Implement Giscus vào Gatsby

Chúng ta phải có 3 thứ:

  • Blog, dĩ nhiên rồi
  • Github account
  • 1 public repo (ở đây mình dùng luôn repo public của blog trên github page)

Đầu tiên, phải thêm app Giscus vào repo githubpage của các bạn trước:

Vào phần settings trên repo public mà bạn muốn dùng, bật tính năng discussion.

Truy cập vào đây: https://github.com/apps/giscus/installations/new, chọn repository bạn muốn, grant quyền cho em nó.

cài giscus

Config widget của giscus:

  1. Vào https://giscus.app/
  2. Config các option như language, theme, position theo ý thích, có thể tạo thêm 1 category discussion là “Comments” cho dễ phân loại.

Config giscus options

  1. Ta nhận được 1 widget như sau:

Giscus widget

Đưa widget vào Gatsby Cái widget mà trên giscus cung cấp chỉ có thể nhúng trên 1 trang html bình thường, nhưng Gatsby là SSG sử dụng React, do đó chúng ta phải cài thêm 1 thư viện:

yarn add @giscus/react

Thêm component comment, cũng thêm chút styling css:

import React, { FunctionComponent } from "react";
import styled from "styled-components";
import Giscus from "@giscus/react";

const PostComment = styled.div`
  margin: 2rem auto;
  max-width: 800px;
`;
const Comments: FunctionComponent = () => (
  <PostComment>
    <Giscus
      repo="devgiangho/kiendinh.space"
      repoId="repoId"
      category="Comments"
      categoryId="categoryId"
      mapping="url"
      strict="0"
      reactionsEnabled="1"
      emitMetadata="0"
      inputPosition="top"
      theme="preferred_color_scheme"
      lang="en"
      loading="lazy"
      cross-origin="anonymous"
    />
  </PostComment>
);

export default Comments;

Giới hạn các domain được load comments:

Để ngăn chặn các trang khác lấy comment từ web bạn, tạo 1 file giscus.json trong repo public mà chúng ta dùng để connect discussion, thêm origin hoặc regex cho nó:

{
  "origins": ["https://kiendinh.space"]
}

Các trang không được phép show comment sẽ bị chặn:

Giscus chặn comment

Thông báo khi có comment mới: Email khi có comment

Bug: Mình gặp 1 bug lúc deploy là không thể login vào giscus iframe. Lý do là bởi vì Gatsby đã tự động remove trailing slash, do đó nó auto remove các query param phía sau.

Ví dụ url của mình dạng kiendinh.space/abc/?giscus=xxxx, thì gatsby auto remove slash, chỉ còn kiendinh.space/abc

Do đó mình phải tắt chế độ này bằng cách thêm 1 option vào gatsby-config:

trailingSlash: "always"

Bug của React

Do thèn quỷ React hay Gatsby routing, hoặc do bản thân giscus-react bị lỗi thì khi đã deploy được vài hôm, mình phát hiện ra thi thoảng iframe comment không được load lại, do đó các comment của bài này lại được hiển thị ở bài khác. Vì vậy mình fix lại bằng cách tự thêm element như sau:

const Comments: FunctionComponent = () => {
  const divRef = useRef(null);

  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://giscus.app/client.js";
    script.dataset.repo = "devgiangho/kiendinh.space";
    script.dataset.repoId = "MDEwOlJlcG9zaXRvcnkyMTI3MzU1ODA=";
    script.dataset.category = "Comments";
    script.dataset.categoryId = "DIC_kwDODK4WXM4CU-Zi";
    script.dataset.mapping = "title";
    script.dataset.reactionsEnabled = "1";
    script.dataset.emitMetadata = "0";
    script.dataset.theme = "light";
    script.dataset.lang = "en";
    script.crossOrigin = "anonymous";
    script.async = true;
    divRef?.current?.appendChild(script);

    return () => {
      const scripts = divRef?.current?.getElementsByTagName("script");
      if (!scripts){
        return;
      }
      for (let i = 0; i < scripts.length; i++) {
        if (scripts[i].src.startsWith("https://giscus.app")) {
          scripts[i].remove();
        }
      }
    };
  }, []);
  return (
    <PostComment>
      <div ref={divRef}></div>
    </PostComment>
  );
};

export default Comments;

Tối ưu để giscus được prefetch trước:

Để lúc user đang đọc bài, chưa kéo tới phần comment thì các script cũng đã được load sẵn, mình có follow cái blog này1.

<link rel="preconnect" href="https://giscus.app"/>
<link rel="dns-prefetch" href="https://giscus.app"/>

Kết quả các bạn có thể thử luôn ở phía bên dưới ⬇️⬇️

Published under Web on .

Kiên Đinh

Bần đạo là Kiên Đinh, một Developer. Ta viết blog này với mục đích chia sẻ những kinh nghiệm của bản thân đối với coding chi đạo.