view/comment/artalk.jsx

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

/**
 * Artalk comment JSX component.
 *
 * @see https://artalk.js.org/guide/frontend/config.html
 * @example
 * <Artalk
 *     server="https://artalk.example.com"
 *     site="******"
 *     pageKey="/path/to/page/"
 *     pageTitle="******"
 *     locale="en"
 *     darkMode={false}
 *     jsUrl="/path/to/Artalk.js"
 *     cssUrl="/path/to/Artalk.css" />
 */
class Artalk extends Component {
  render() {
    const {
      server,
      site,
      pageKey,
      pageTitle,
      locale,
      darkMode,
      preferRemoteConf,
      jsUrl,
      cssUrl,
      localeUrls = [],
    } = this.props;
    if (!server) {
      return (
        <div class="notification is-danger">
          You forgot to set the <code>server</code> for Artalk. Please set it in{' '}
          <code>_config.yml</code>.
        </div>
      );
    }
    // Without the dark_mode option, follow the appearance of the page
    const followAppearance = darkMode === null || typeof darkMode === 'undefined';
    const darkModeOption = followAppearance
      ? `${getAppearance()} === 'auto' ? 'auto' : ${isDark()}`
      : toJs(darkMode);
    // PJAX replaces the element of the previous instance, so destroy it before initializing again
    let js = `if (!container || !container.isConnected) return;
        if (window.__artalkInstance) {
            window.__artalkInstance.destroy();
        }
        window.__artalkInstance = Artalk.init({
            el: '#artalk-thread',
            server: ${toJs(server)},
            ${site ? `site: ${toJs(site)},` : ''}
            ${pageKey ? `pageKey: ${toJs(pageKey)},` : ''}
            ${pageTitle ? `pageTitle: ${toJs(pageTitle)},` : ''}
            ${locale ? `locale: ${toJs(locale)},` : ''}
            ${typeof preferRemoteConf === 'boolean' ? `preferRemoteConf: ${preferRemoteConf},` : ''}
            darkMode: ${darkModeOption},
        });`;
    if (followAppearance) {
      js += `
        var instance = window.__artalkInstance;
        var observer = new MutationObserver(function () {
            instance.update({ darkMode: ${darkModeOption} });
        });
        observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-appearance'] });
        instance.on('unmounted', function () { observer.disconnect(); });`;
    }
    return (
      <>
        <div id="artalk-thread"></div>
        <link rel="stylesheet" href={cssUrl} media="print" onload="this.media='all'" />
        <script
          dangerouslySetInnerHTML={{
            __html: `(function () {
          var container = document.getElementById('artalk-thread');
          ${loadScripts([jsUrl].concat(localeUrls), js)}
        })();`,
          }}></script>
      </>
    );
  }
}

/**
 * Cacheable Artalk 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
 * <Artalk.Cacheable
 *     comment={{
 *         server: "https://artalk.example.com",
 *         site: "******",
 *         locale: "en",
 *         dark_mode: false
 *     }}
 *     page={{ title: "******" }}
 *     helper={{ cdn: function() {...} }} />
 */
Artalk.Cacheable = cacheComponent(Artalk, 'comment.artalk', (props) => {
  const { comment, helper, page } = props;
  // The full bundle includes en and zh-CN only. Other published dictionaries register themselves
  // on window.ArtalkI18n before initialization. For auto, make every supported dictionary available.
  const externalLocales = ['zh-TW', 'ja', 'ko', 'fr', 'ru', 'tr'];
  const language = String(comment.locale || '').toLowerCase();
  const localeNames = externalLocales.filter((name) => {
    return (
      language === 'auto' ||
      language === name.toLowerCase() ||
      language.startsWith(name.toLowerCase() + '-')
    );
  });

  return {
    server: comment.server,
    site: comment.site,
    pageKey: comment.page_key,
    pageTitle: page.title,
    locale: comment.locale,
    darkMode: comment.dark_mode,
    preferRemoteConf: comment.prefer_remote_conf,
    jsUrl: helper.cdn('artalk', '2.10.0', 'dist/Artalk.js'),
    cssUrl: helper.cdn('artalk', '2.10.0', 'dist/Artalk.css'),
    localeUrls: localeNames.map((name) => helper.cdn('artalk', '2.10.0', `dist/i18n/${name}.js`)),
  };
});

module.exports = Artalk;