view/comment/giscus.jsx

/**
 * Giscus comment JSX component.
 * @module view/comment/giscus
 */
const { Component } = require('inferno');
const { cacheComponent } = require('../../util/cache');
const { appendScript, toJs } = require('../../util/script');
const { isDark } = require('../../util/appearance');

/**
 * Giscus comment JSX component.
 *
 * @see https://giscus.app/
 * @example
 * <Giscus
 *     repo="usr/repo"
 *     repoId="X_xxxxxxxxxx"
 *     category="******"
 *     categoryId="XXX_xxxxxxxxxxxxxxxx"
 *     mapping="******"
 *     strict={0}
 *     reactionsEnabled={0}
 *     emitMetadata={0}
 *     inputPosition="******"
 *     theme="******"
 *     darkTheme="******"
 *     lang="******"
 *     lazy="******" />
 */
class Giscus extends Component {
  render() {
    const {
      repo,
      repoId,
      category,
      categoryId,
      mapping,
      term,
      strict,
      reactionsEnabled,
      emitMetadata,
      inputPosition,
      theme,
      darkTheme,
      customThemeCss,
      lang,
      lazy,
    } = this.props;
    if (!repo || !repoId || !categoryId) {
      return (
        <div class="notification is-danger">
          You forgot to set the <code>repo</code>, <code>repoId</code>, or <code>categoryId</code>{' '}
          for Giscus. Please set it in <code>_config.yml</code>.
        </div>
      );
    }
    if ((mapping === 'specific' || mapping === 'number') && !term) {
      return (
        <div class="notification is-danger">
          You set <code>mapping</code> to <code>specific</code> or <code>number</code>, but did not
          set <code>term</code> for Giscus. Please set <code>term</code> in <code>_config.yml</code>
          .
        </div>
      );
    }
    const config = {};
    config['data-repo'] = repo;
    config['data-repo-id'] = repoId;
    config['data-category-id'] = categoryId;
    if (category) {
      config['data-category'] = category;
    } else {
      config['data-category'] = 'Announcements';
    }
    if (mapping) {
      config['data-mapping'] = mapping;
    } else {
      config['data-mapping'] = 'pathname';
    }
    if (term) {
      config['data-term'] = term;
    }
    if (strict) {
      config['data-strict'] = 1;
    } else {
      config['data-strict'] = 0;
    }
    if (reactionsEnabled) {
      config['data-reactions-enabled'] = 1;
    } else {
      config['data-reactions-enabled'] = 0;
    }
    if (emitMetadata) {
      config['data-emit-metadata'] = 1;
    } else {
      config['data-emit-metadata'] = 0;
    }
    if (inputPosition) {
      config['data-input-position'] = inputPosition;
    } else {
      config['data-input-position'] = 'top';
    }
    // Use the dark theme when the page looks dark, unless the theme is custom
    const dynamicConfig = {};
    if (theme === 'custom') {
      if (customThemeCss) {
        config['data-theme'] = customThemeCss;
      } else {
        return (
          <div class="notification is-danger">
            You set <code>theme</code> to <code>custom</code>, but did not apply a{' '}
            <code>customThemeCss</code> for Giscus. Please set it in <code>_config.yml</code>.
          </div>
        );
      }
    } else {
      dynamicConfig['data-theme'] = `${isDark()} ? ${toJs(
        darkTheme || 'noborder_dark',
      )} : ${toJs(theme || 'noborder_light')}`;
    }
    if (lang) {
      config['data-lang'] = lang;
    } else {
      config['data-lang'] = 'en';
    }
    if (lazy) {
      config['data-loading'] = 'lazy';
    }
    config.crossorigin = 'anonymous';
    // Giscus puts its iframe into the element with the giscus class, so add its script to <head> to
    // keep the number of scripts in the comment section the same for PJAX
    const js = appendScript('head', 'https://giscus.app/client.js', config, dynamicConfig);
    return (
      <>
        <div id="giscus-thread" class="giscus"></div>
        <script dangerouslySetInnerHTML={{ __html: js }}></script>
      </>
    );
  }
}

/**
 * Cacheable Giscus 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
 * <Giscus.Cacheable
 *     comment={{
 *         repo: "usr/repo"
 *         repoId: "X_xxxxxxxxxx"
 *         category: "******"
 *         categoryId: "XXX_xxxxxxxxxxxxxxxx"
 *         mapping: "******"
 *         strict: {false}
 *         reactionsEnabled: {false}
 *         emitMetadata: {false}
 *         inputPosition: "******"
 *         theme: "******"
 *         darkTheme: "******"
 *         lang: "******"
 *         lazy: "******"
 *     }} />
 */
Giscus.Cacheable = cacheComponent(Giscus, 'comment.giscus', (props) => {
  const {
    repo,
    repoId,
    category,
    categoryId,
    mapping,
    term,
    strict,
    reactionsEnabled,
    emitMetadata,
    inputPosition,
    theme,
    darkTheme,
    customThemeCss,
    lang,
    lazy,
  } = props.comment;

  return {
    repo,
    repoId,
    category,
    categoryId,
    mapping,
    term,
    strict,
    reactionsEnabled,
    emitMetadata,
    inputPosition,
    theme,
    darkTheme,
    customThemeCss,
    lang,
    lazy,
  };
});

module.exports = Giscus;