view/comment/gitalk.jsx

/**
 * Gitalk comment JSX component.
 * @module view/comment/gitalk
 */
const crypto = require('crypto');
const { Component } = require('inferno');
const { cacheComponent } = require('../../util/cache');
const loadScripts = require('../../util/script');
const { toJs } = loadScripts;
const { isDark } = require('../../util/appearance');

/**
 * Gitalk comment JSX component.
 *
 * @see https://github.com/gitalk/gitalk
 * @example
 * <Gitalk
 *     id="******",
 *     repo="******",
 *     owner="******",
 *     admin={["******"]},
 *     clientId="******",
 *     backend="issues",
 *     createIssueManually={false},
 *     distractionFreeMode={false},
 *     pagerDirection="last",
 *     perPage={10},
 *     proxy="******",
 *     enableHotKey={true},
 *     language="zh-CN",
 *     jsUrl="/path/to/gitalk.js",
 *     cssUrl="/path/to/gitalk.css" />
 */
class Gitalk extends Component {
  render() {
    const {
      id,
      repo,
      owner,
      admin,
      clientId,
      backend = 'issues',
      number,
      category,
      createIssueManually = true,
      distractionFreeMode = false,
      pagerDirection = 'last',
      perPage = 10,
      proxy,
      enableHotKey = true,
      language,
      jsUrl,
      cssUrl,
    } = this.props;

    if (!id || !repo || !owner || !admin || !clientId || !proxy) {
      return (
        <div class="notification is-danger">
          You forgot to set the <code>owner</code>, <code>admin</code>, <code>repo</code>,
          <code>client_id</code>, or <code>proxy</code> for Gitalk. Please set it in{' '}
          <code>_config.yml</code>.
        </div>
      );
    }
    const js = `if (!container || !container.isConnected || document.getElementById('comment-container') !== container) return;
        if (window.__gitalkInstance) window.__gitalkInstance.destroy();
        var gitalk = new Gitalk({
            id: ${toJs(id)},
            repo: ${toJs(repo)},
            owner: ${toJs(owner)},
            clientID: ${toJs(clientId)},
            backend: ${toJs(backend)},
            ${number ? `number: ${toJs(number)},` : ''}
            ${category ? `category: ${toJs(category)},` : ''}
            admin: ${toJs(admin)},
            createIssueManually: ${!!createIssueManually},
            distractionFreeMode: ${!!distractionFreeMode},
            perPage: ${toJs(perPage)},
            sortDirection: ${toJs(pagerDirection)},
            ${proxy ? `proxy: ${toJs(proxy)},` : ''}
            mode: 'inline',
            maxHeight: null,
            stickyComposer: false,
            enableHotKey: ${!!enableHotKey},
            ${language ? `language: ${toJs(language)},` : ''}
        })
        gitalk.render('comment-container');
        window.__gitalkInstance = gitalk;
        var media = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
        function appearance() { container.setAttribute('data-theme', ${isDark()} ? 'dark' : 'light'); }
        appearance();
        var observer = new MutationObserver(appearance);
        observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-appearance'] });
        if (media && media.addEventListener) media.addEventListener('change', appearance);
        var destroy = gitalk.destroy;
        gitalk.destroy = function () {
            document.removeEventListener('hexo:page:before-replace', gitalk.destroy);
            observer.disconnect();
            if (media && media.removeEventListener) media.removeEventListener('change', appearance);
            destroy.call(gitalk);
            if (window.__gitalkInstance === gitalk) window.__gitalkInstance = null;
        };
        document.addEventListener('hexo:page:before-replace', gitalk.destroy);`;
    return (
      <>
        <div id="comment-container"></div>
        <link rel="stylesheet" href={cssUrl} media="print" onload="this.media='all'" />
        <script
          dangerouslySetInnerHTML={{
            __html: `(function () {
          var container = document.getElementById('comment-container');
          ${loadScripts(jsUrl, js)}
        })();`,
          }}></script>
      </>
    );
  }
}

/**
 * Cacheable Gitalk comment JSX component.
 * <p>
 * This class is supposed to be used in combination with the <code>locals</code> hexo filter
 * ({@link module:hexo/filter/locals}).
 *
 * @see module:util/cache.cacheComponent
 * @example
 * <Gitalk.Cacheable
 *     comment={{
 *         repo: '******',
 *         owner: '******',
 *         admin: ['******'],
 *         client_id: '******',
 *         backend: 'issues',
 *         create_issue_manually: false,
 *         distraction_free_mode: false,
 *         pager_direction: 'last',
 *         per_page: 10,
 *         proxy: '******',
 *         enable_hotkey: true,
 *         language: 'zh-CN'
 *     }}
 *     page={{ path: '/path/to/page' }}
 *     helper={{ cdn: function() {...} }} />
 */
Gitalk.Cacheable = cacheComponent(Gitalk, 'comment.gitalk', (props) => {
  const { helper, comment } = props;

  // FIXME: config name change
  const id = crypto.createHash('md5').update(props.page.path).digest('hex');
  return {
    id,
    repo: comment.repo,
    owner: comment.owner,
    admin: comment.admin,
    clientId: comment.client_id,
    backend: comment.backend || 'issues',
    number: comment.number,
    category: comment.category,
    createIssueManually: comment.create_issue_manually,
    distractionFreeMode: comment.distraction_free_mode,
    pagerDirection: comment.pager_direction,
    perPage: comment.per_page,
    proxy: comment.proxy,
    enableHotKey: comment.enable_hotkey,
    language: comment.language,
    cssUrl: helper.cdn('@aifire/gitalk', '2.0.0-beta.2', 'dist/gitalk.css'),
    jsUrl: helper.cdn('@aifire/gitalk', '2.0.0-beta.2', 'dist/gitalk.js'),
  };
});

module.exports = Gitalk;