/**
* Waline comment JSX component.
* @module view/comment/waline
*/
const { Component } = require('inferno');
const { cacheComponent } = require('../../util/cache');
const { toJs } = require('../../util/script');
const { getAppearance } = require('../../util/appearance');
/**
* Waline comment JSX component.
*
* @see https://waline.js.org/guide/get-started.html
* @example
* <Waline
* serverURL="https://path/to/waline/server"
* path="window.location.pathname"
* lang="zh-CN"
* locale={{placeholder: "", ...}}
* emoji={["https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo"]}
* dark="auto"
* meta={["nick", "mail", "link"]}
* requiredMeta={[]}
* login="enable"
* wordLimit={0},
* pageSize={10}
* imageUploader={true}
* highlighter={true}
* texRenderer={false}
* search={true}
* pageview={false}
* comment={false}
* copyright={true}
* jsUrl="/path/to/Waline.js" />
*/
class Waline extends Component {
render() {
const {
serverURL,
path,
lang = 'zh-CN',
locale,
emoji = ['https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo'],
dark,
meta = ['nick', 'mail', 'link'],
requiredMeta = [],
login = 'enable',
wordLimit = 0,
pageSize = 10,
imageUploader,
highlighter,
texRenderer,
search,
pageview = false,
comment = false,
copyright = true,
jsUrl,
cssUrl,
} = this.props;
if (!serverURL) {
return (
<div class="notification is-danger">
You forgot to set the <code>server_url</code> for Waline. Please set it in{' '}
<code>_config.yml</code>.
</div>
);
}
// Without the dark option, follow the appearance of the page: the system color scheme for the auto
// appearance, or the data-appearance="dark" attribute of <html> otherwise
const followAppearance = dark === null || typeof dark === 'undefined' || dark === '';
const darkOption = followAppearance
? `${getAppearance()} === 'auto' ? 'auto' : 'html[data-appearance="dark"]'`
: toJs(dark);
// A path, such as /posts/hello/, or a JavaScript expression, such as window.location.pathname
let pathCode = path || 'window.location.pathname';
if (pathCode.startsWith('/')) {
pathCode = toJs(pathCode);
}
// Use a classic script with a dynamic import instead of a module script, so that PJAX, which only
// runs classic scripts, initializes Waline again after switching pages. The previous instance,
// whose element PJAX has replaced, is destroyed first.
const appearanceObserver = followAppearance
? `var instance = window.__walineInstance;
if (instance) {
var observer = new MutationObserver(function () {
instance.update({ dark: ${darkOption}, path: path });
});
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-appearance'] });
var destroy = instance.destroy;
instance.destroy = function () {
observer.disconnect();
destroy.call(instance);
};
}`
: '';
const js = `(function () {
var container = document.getElementById('waline-thread');
var path = ${pathCode};
return import(${toJs(jsUrl)}).then(function (Waline) {
if (!container || !container.isConnected || document.getElementById('waline-thread') !== container) return;
if (window.__walineInstance) {
window.__walineInstance.destroy();
}
window.__walineInstance = Waline.init({
el: '#waline-thread',
serverURL: ${toJs(serverURL)},
path: path,
${lang ? `lang: ${toJs(lang)},` : ''}
${locale ? `locale: ${toJs(locale)},` : ''}
${emoji !== null && typeof emoji !== 'undefined' ? `emoji: ${toJs(emoji)},` : ''}
dark: ${darkOption},
${meta ? `meta: ${toJs(meta)},` : ''}
${Array.isArray(requiredMeta) ? `requiredMeta: ${toJs(requiredMeta)},` : ''}
${login ? `login: ${toJs(login)},` : ''}
${wordLimit ? `wordLimit: ${toJs(wordLimit)},` : ''}
${pageSize ? `pageSize: ${toJs(pageSize)},` : ''}
${imageUploader === false ? `imageUploader: false,` : ''}
${highlighter === false ? `highlighter: false,` : ''}
${texRenderer === false ? `texRenderer: false,` : ''}
${search === false ? `search: false,` : ''}
${typeof pageview !== 'undefined' ? `pageview: ${toJs(pageview)},` : ''}
${typeof comment !== 'undefined' ? `comment: ${toJs(comment)},` : ''}
noCopyright: ${toJs(copyright === false)},
});
${appearanceObserver}
});
})();`;
return (
<>
<div id="waline-thread" class="content"></div>
<link rel="stylesheet" href={cssUrl} media="print" onload="this.media='all'" />
<script dangerouslySetInnerHTML={{ __html: js }} />
</>
);
}
}
/**
* Cacheable Waline 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
* <Waline.Cacheable
* comment={{
* server_url: "https://path/to/waline/server",
* path: "window.location.pathname",
* lang: "zh-CN",
* locale: {placeholder: "", ...},
* emoji: "https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo",
* dark: "",
* meta: ["nick", "mail", "link"],
* required_meta: [],
* login: false,
* word_limit: 0,
* page_size: 10,
* image_uploader: false,
* highlighter: false,
* tex_renderer: false,
* search: false,
* pageview: false,
* comment: false,
* copyright: true,
* }}
* helper={{ cdn: function() {...} }} />
*/
Waline.Cacheable = cacheComponent(Waline, 'comment.waline', (props) => {
const { comment, helper, page, config } = props;
const language = comment.lang || page.lang || page.language || config.language;
return {
serverURL: comment.server_url,
path: comment.path,
lang: (Array.isArray(language) ? language[0] : language) || 'zh-CN',
locale: comment.locale,
emoji: comment.emoji,
dark: comment.dark,
meta: comment.meta,
requiredMeta: comment.required_meta,
login: comment.login,
wordLimit: comment.word_limit,
pageSize: comment.page_size,
// These options of the theme config disable the features when they are true, which Waline does
// when its options are false
imageUploader: comment.image_uploader === true ? false : undefined,
highlighter: comment.highlighter === true ? false : undefined,
texRenderer: comment.tex_renderer === true ? false : undefined,
search: comment.search === true ? false : undefined,
pageview: comment.pageview,
comment: comment.comment,
copyright: comment.copyright,
jsUrl: helper.cdn('@waline/client', '3.16.1', 'dist/waline.js'),
cssUrl: helper.cdn('@waline/client', '3.16.1', 'dist/waline.css'),
};
});
module.exports = Waline;