refactor(layout): article to jsx

This commit is contained in:
ppoffice 2019-12-23 16:18:59 -05:00
parent 467636b50a
commit 2dae089370
23 changed files with 300 additions and 186 deletions

View File

@ -22,9 +22,11 @@ class ChangeYan extends Component {
}
module.exports = cacheComponent(ChangeYan, 'comment.changyan', props => {
const { comment, page } = props;
return {
appId: props.appid,
conf: props.conf,
path: props.page.path
appId: comment.appid,
conf: comment.conf,
path: page.path
};
});

View File

@ -32,10 +32,12 @@ class Disqus extends Component {
}
module.exports = cacheComponent(Disqus, 'comment.disqus', props => {
const { comment, page } = props;
return {
path: props.page.path,
shortname: props.shortname,
disqusId: props.page.disqusId,
permalink: props.page.permalink
path: page.path,
shortname: comment.shortname,
disqusId: page.disqusId,
permalink: page.permalink
};
});

View File

@ -10,7 +10,7 @@ class Facebook extends Component {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/${(language || 'en').split('-').join('_')}/sdk.js#xfbml=1&version=v2.8";
js.src = "//connect.facebook.net/${language.split('-').join('_')}/sdk.js#xfbml=1&version=v2.8";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));`;
return <Fragment>
@ -21,8 +21,10 @@ class Facebook extends Component {
}
module.exports = cacheComponent(Facebook, 'comment.facebook', props => {
const { config, page } = props;
return {
language: props.language,
permalink: props.page.permalink
language: page.lang || page.language || config.language || 'en',
permalink: page.permalink
};
});

View File

@ -47,18 +47,20 @@ class Gitalk extends Component {
}
module.exports = 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: props.repo,
owner: props.owner,
admin: props.admin,
clientId: props.clientId,
clientSecret: props.clientSecret,
createIssueManually: props.createIssueManually,
distractionFreeMode: props.distractionFreeMode,
cssUrl: props.cdn('gitalk', '1.4.1', 'dist/gitalk.css'),
jsUrl: props.cdn('gitalk', '1.4.1', 'dist/gitalk.min.js')
repo: comment.repo,
owner: comment.owner,
admin: comment.admin,
clientId: comment.clientId,
clientSecret: comment.clientSecret,
createIssueManually: comment.createIssueManually,
distractionFreeMode: comment.distractionFreeMode,
cssUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.css'),
jsUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.min.js')
};
});

View File

@ -41,12 +41,14 @@ class Gitment extends Component {
}
module.exports = cacheComponent(Gitment, 'comment.gitment', props => {
const { comment } = props;
const id = crypto.createHash('md5').update(props.page.path).digest('hex');
return {
id,
repo: props.repo,
owner: props.owner,
clientId: props.client_id,
clientSecret: props.client_secret
repo: comment.repo,
owner: comment.owner,
clientId: comment.client_id,
clientSecret: comment.client_secret
};
});

View File

@ -20,7 +20,9 @@ class Isso extends Component {
}
module.exports = cacheComponent(Isso, 'comment.isso', props => {
const { comment } = props;
return {
url: props.url
url: comment.url
};
});

View File

@ -31,7 +31,9 @@ class LiveRe extends Component {
}
module.exports = cacheComponent(LiveRe, 'comment.livere', props => {
const { comment } = props;
return {
uid: props.uid
uid: comment.uid
};
});

View File

@ -30,11 +30,13 @@ class Valine extends Component {
}
module.exports = cacheComponent(Valine, 'comment.valine', props => {
const { comment } = props;
return {
appId: props.app_id,
appKey: props.app_key,
notify: props.notify,
verify: props.verify,
placeholder: props.placeholder
appId: comment.app_id,
appKey: comment.app_key,
notify: comment.notify,
verify: comment.verify,
placeholder: comment.placeholder
};
});

View File

@ -1,127 +0,0 @@
<% post = post(); %>
<div class="card">
<% if (has_thumbnail(post)) { %>
<div class="card-image">
<%- index ? '<a href="' + url_for(post.link ? post.link : post.path) + '"' : '<span ' %> class="image is-7by1">
<img class="thumbnail" src="<%= get_thumbnail(post) %>" alt="<%= post.title %>">
<%- index ? '</a>' : '</span>' %>
</div>
<% } %>
<div class="card-content article <%= post.hasOwnProperty('direction') ? post.direction : '' %>">
<% if (post.layout != 'page') { %>
<div class="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto">
<div class="level-left">
<time class="level-item has-text-grey" datetime="<%= date_xml(post.date) %>"><%= date(post.date) %></time>
<% if (post.categories && post.categories.length) { %>
<div class="level-item">
<%- list_categories(post.categories, {
class: 'has-link-grey ',
show_count: false,
style: 'none',
separator: '&nbsp;/&nbsp;'
}) %>
</div>
<% } %>
<% if (!has_config('article.readtime') || get_config('article.readtime') === true) { %>
<span class="level-item has-text-grey">
<% const words = word_count(post._content); %>
<% const time = duration((words / 150.0) * 60, 'seconds') %>
<%= `${ time.locale(get_config('language', 'en')).humanize() } ${ __('article.read')} (${ __('article.about') } ${ words } ${ __('article.words') })` %>
</span>
<% } %>
<% if (!index && (has_config('plugins.busuanzi') ? get_config('plugins.busuanzi') : false)) { %>
<span class="level-item has-text-grey" id="busuanzi_container_page_pv">
<i class="far fa-eye"></i>
<%- _p('plugin.visit', '<span id="busuanzi_value_page_pv">0</span>') %>
</span>
<% } %>
</div>
</div>
<% } %>
<h1 class="title is-size-3 is-size-4-mobile has-text-weight-normal">
<% if (index) { %>
<a class="has-link-black-ter" href="<%- url_for(post.link ? post.link : post.path) %>"><%= post.title %></a>
<% } else { %>
<%= post.title %>
<% } %>
</h1>
<div class="content">
<%- index && post.excerpt ? post.excerpt : post.content %>
</div>
<% if (!index && post.tags && post.tags.length) { %>
<div class="level is-size-7 is-uppercase">
<div class="level-start">
<div class="level-item">
<span class="is-size-6 has-text-grey has-mr-7">#</span>
<%- list_tags(post.tags, {
class: 'has-link-grey ',
show_count: false,
style: 'link'
}) %>
</div>
</div>
</div>
<% } %>
<% if (index && post.excerpt) { %>
<div class="level is-mobile">
<div class="level-start">
<div class="level-item">
<a class="button is-size-7 is-light" href="<%- url_for(post.path) %>#more"><%= __('article.more') %></a>
</div>
</div>
</div>
<% } %>
<% if (!index && has_config('share.type')) { %>
<%- _partial('share/' + get_config('share.type')) %>
<% } %>
</div>
</div>
<% const services = has_config('donate') ? get_config('donate') : []; %>
<% if (!index && services.length > 0) { %>
<div class="card">
<div class="card-content">
<h3 class="menu-label has-text-centered"><%= __('donate.title') %></h3>
<div class="buttons is-centered">
<% for (let service of services) {
const type = get_config_from_obj(service, 'type');
if (type !== null) { %>
<%- _partial('donate/' + type, { type, service }) %>
<% }
} %>
</div>
</div>
</div>
<% } %>
<% if (!index && (post.prev || post.next)) { %>
<div class="card card-transparent">
<div class="level post-navigation is-flex-wrap is-mobile">
<% if (post.prev){ %>
<div class="level-start">
<a class="level level-item has-link-grey <%= !post.prev ? 'is-hidden-mobile' : '' %> article-nav-prev" href="<%- url_for(post.prev.path) %>">
<i class="level-item fas fa-chevron-left"></i>
<span class="level-item"><%= post.prev.title %></span>
</a>
</div>
<% } %>
<% if (post.next){ %>
<div class="level-end">
<a class="level level-item has-link-grey <%= !post.next ? 'is-hidden-mobile' : '' %> article-nav-next" href="<%- url_for(post.next.path) %>">
<span class="level-item"><%= post.next.title %></span>
<i class="level-item fas fa-chevron-right"></i>
</a>
</div>
<% } %>
</div>
</div>
<% } %>
<% if (!index && has_config('comment.type')) { %>
<div class="card">
<div class="card-content">
<h3 class="title is-5 has-text-weight-normal"><%= __('article.comments') %></h3>
<%- _partial('comment/' + get_config('comment.type')) %>
</div>
</div>
<% } %>

125
layout/common/article.jsx Normal file
View File

@ -0,0 +1,125 @@
'use strict';
const moment = require('moment');
const { Component, Fragment } = require('inferno');
const Share = require('./share');
const Donates = require('./donates');
const Comment = require('./comment');
/**
* Get the word count of a paragraph.
*/
function getWordCount(content) {
content = content.replace(/<\/?[a-z][^>]*>/gi, '');
content = content.trim();
return content ? (content.match(/[\u00ff-\uffff]|[a-zA-Z]+/g) || []).length : 0;
}
module.exports = class extends Component {
render() {
const { config, helper, page, index } = this.props;
const { article, plugins } = config;
const { has_thumbnail, get_thumbnail, url_for, date, date_xml, __, _p } = helper;
const language = page.lang || page.language || config.language || 'en';
return <Fragment>
{/* Main content */}
<div className="card">
{/* Thumbnail */}
{has_thumbnail(page) ? <div className="card-image">
{index ? <a href={url_for(page.link || page.path || page.permalink)} className="image is-7by1">
<img className="thumbnail" src={get_thumbnail(page)} alt={page.title || get_thumbnail(page)} />
</a> : <span className="image is-7by1">
<img className="thumbnail" src={get_thumbnail(page)} alt={page.title || get_thumbnail(page)} />
</span>}
</div> : null}
{/* Metadata */}
<div className={`card-content article${Object.prototype.hasOwnProperty.call(page, 'direction') ? ' ' + page.direction : ''}`}>
{page.layout !== 'page' ? <div className="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto">
<div className="level-left">
{/* Date */}
<time className="level-item has-text-grey" datetime={date_xml(page.date)}>{date(page.date)}</time>
{/* Categories */}
{page.categories && page.categories.length ? <div className="level-item">
{(() => {
const categories = [];
page.categories.forEach((category, i) => {
categories.push(<a className="has-link-grey" href={category.url}>{category.name}</a>);
if (i < page.categories.length - 1) {
categories.push('/');
}
});
return categories;
})()}
</div> : null}
{/* Read time */}
{article && article.readtime && article.readtime === true ? <span className="level-item has-text-grey">
{(() => {
const words = getWordCount(page._content);
const time = moment.duration((words / 150.0) * 60, 'seconds');
return `${time.locale(language).humanize()} ${__('article.read')} (${__('article.about')} ${words} ${__('article.words')})`;
})()}
</span> : null}
{/* Visitor counter */}
{plugins && plugins.busuanzi === true ? <span className="level-item has-text-grey" id="busuanzi_container_page_pv"
dangerouslySetInnerHTML={{
__html: _p('plugin.visit', '<i className="far fa-eye"></i><span id="busuanzi_value_page_pv">0</span>')
}}></span> : null}
</div>
</div> : null}
{/* Title */}
<h1 className="title is-size-3 is-size-4-mobile has-text-weight-normal">
{index ? <a className="has-link-black-ter" href={url_for(page.link || page.path)}>{page.title}</a> : page.title}
</h1>
{/* Content/Excerpt */}
<div className="content">
{index && page.excerpt ? page.excerpt : page.content}
</div>
{/* Tags */}
{!index && Array.isArray(page.tags) && page.tags.length ? <div className="level is-size-7 is-uppercase">
<div className="level-start">
<div className="level-item">
<span className="is-size-6 has-text-grey has-mr-7">#</span>
{page.tags.map(tag => {
return <a href="has-link-grey" rel="tag" href={url_for(tag.path)}>{tag.name}</a>;
})}
</div>
</div>
</div> : null}
{/* "Read more" button */}
{index && page.excerpt ? <div className="level is-mobile">
<div className="level-start">
<div className="level-item">
<a className="button is-size-7 is-light" href={`${url_for(page.path)}#more`}>{__('article.more')}</a>
</div>
</div>
</div> : null}
{/* Share button */}
{!index ? <Share config={config} page={page} helper={helper} /> : null}
</div>
</div>
{/* Donate button */}
{!index ? <Donates config={config} helper={helper} /> : null}
{/* Post navigation */}
{!index && (page.prev || page.next) ? <div className="card card-transparent">
<div className="level post-navigation is-flex-wrap is-mobile">
{page.prev ? <div className="level-start">
<a className={`level level-item has-link-grey${!page.prev ? ' is-hidden-mobile' : ''} article-nav-prev`} href={url_for(page.prev.path)}>
<i className="level-item fas fa-chevron-left"></i>
<span className="level-item">{page.prev.title}</span>
</a>
</div> : null}
{page.next ? <div className="level-end">
<a className={`level level-item has-link-grey${!page.next ? ' is-hidden-mobile' : ''} article-nav-next`} href={url_for(page.next.path)}>
<span className="level-item">{page.next.title}</span>
<i className="level-item fas fa-chevron-right"></i>
</a>
</div> : null}
</div>
</div> : null}
{/* Comment */}
{!index ? <Comment config={config} page={page} helper={helper} /> : null}
</Fragment>;
}
};

View File

@ -1,8 +0,0 @@
module.exports = (ctx, locals) => {
const { index, post } = locals;
return Object.assign(locals, {
index,
post: () => post,
post_id: post._id
});
}

30
layout/common/comment.jsx Normal file
View File

@ -0,0 +1,30 @@
'use strict';
const logger = require('hexo-log');
const { Component } = require('inferno');
module.exports = class extends Component {
render() {
const { config, page, helper } = this.props;
const { __ } = helper;
const { comment } = config;
if (!comment || typeof comment.type !== 'string') {
return null;
}
return <div className="card">
<div className="card-content">
<h3 className="title is-5 has-text-weight-normal">{__('article.comments')}</h3>
{(() => {
try {
const Comment = require('../comment/' + comment.type);
return <Comment config={config} page={page} helper={helper} comment={comment} />;
} catch (e) {
logger.warn(`Icarus cannot load comment "${comment.type}"`);
return null;
}
})()}
</div>
</div>;
}
};

34
layout/common/donates.jsx Normal file
View File

@ -0,0 +1,34 @@
'use strict';
const logger = require('hexo-log');
const { Component } = require('inferno');
module.exports = class extends Component {
render() {
const { config, helper } = this.props;
const { __ } = helper;
const { donate = [] } = config;
if (!Array.isArray(donate) || !donate.length) {
return null;
}
return <div className="card">
<div className="card-content">
<h3 className="menu-label has-text-centered">{__('donate.title')}</h3>
<div className="buttons is-centered">
{donate.map(service => {
const type = service.type;
if (typeof type === 'string') {
try {
const Donate = require('../donate/' + type);
return <Donate helper={helper} donate={service} />;
} catch (e) {
logger.warn(`Icarus cannot load donate button "${type}"`);
}
}
return null;
})}
</div>
</div>
</div>;
}
};

View File

@ -21,7 +21,7 @@ module.exports = class extends Component {
highlight
} = config;
let hlTheme;
let hlTheme, images;
if (highlight && highlight.enable === false) {
hlTheme = null;
} else if (article && article.highlight && article.hightlight.theme) {
@ -30,13 +30,21 @@ module.exports = class extends Component {
hlTheme = 'atom-one-light';
}
const images = [];
if (page.content && page.content.includes('<img')) {
if (typeof page.og_image === 'string') {
images = [page.og_image];
} else if (helper.has_thumbnail(page)) {
images = [helper.get_thumbnail(page)];
} else if (article && typeof article.og_image === 'string') {
images = [article.og_image];
} else if (page.content && page.content.includes('<img')) {
let img;
images = [];
const imgPattern = /<img [^>]*src=['"]([^'"]+)([^>]*>)/gi;
while ((img = imgPattern.exec(page.content)) !== null) {
images.push(img[1]);
}
} else {
images = [url_for('/images/og_image.png')];
}
return <head>

View File

@ -19,8 +19,8 @@ module.exports = class extends Component {
return <Plugin site={site} config={config} page={page} helper={helper} plugin={plugins[name]} head={head} />;
} catch (e) {
logger.warn(`Icarus cannot load plugin "${name}"`);
return null;
}
return null;
})}
</Fragment>;
}

22
layout/common/share.jsx Normal file
View File

@ -0,0 +1,22 @@
'use strict';
const logger = require('hexo-log');
const { Component } = require('inferno');
module.exports = class extends Component {
render() {
const { config, page, helper } = this.props;
const { share } = config;
if (!share || typeof share.type !== 'string') {
return null;
}
try {
const Share = require('../share/' + share.type);
return <Share config={config} page={page} helper={helper} share={share} />;
} catch (e) {
logger.warn(`Icarus cannot load share button "${share.type}"`);
return null;
}
}
};

View File

@ -23,9 +23,11 @@ class Alipay extends Component {
}
module.exports = cacheComponent(Alipay, 'donate.alipay', props => {
const { donate, helper } = props;
return {
qrcode: props.qrcode,
title: props.__('donate.' + props.type),
url_for: props.url_for
qrcode: donate.qrcode,
title: helper.__('donate.' + donate.type),
url_for: helper.url_for
};
});

View File

@ -22,9 +22,11 @@ class Patreon extends Component {
}
module.exports = cacheComponent(Patreon, 'donate.petreon', props => {
const { donate, helper } = props;
return {
url: props.url,
title: props.__('donate.' + props.type),
url_for: props.url_for
url: donate.url,
title: helper.__('donate.' + donate.type),
url_for: helper.url_for
};
});

View File

@ -29,10 +29,12 @@ class Paypal extends Component {
}
module.exports = cacheComponent(Paypal, 'donate.paypal', props => {
const { donate, helper } = props;
return {
business: props.business,
currencyCode: props.currency_code,
title: props.__('donate.' + props.type),
url_for: props.url_for
business: donate.business,
currencyCode: donate.currency_code,
title: helper.__('donate.' + donate.type),
url_for: helper.url_for
};
});

View File

@ -23,9 +23,11 @@ class Wechat extends Component {
}
module.exports = cacheComponent(Wechat, 'donate.wechat', props => {
const { donate, helper } = props;
return {
qrcode: props.qrcode,
title: props.__('donate.' + props.type),
url_for: props.url_for
qrcode: donate.qrcode,
title: helper.__('donate.' + donate.type),
url_for: helper.url_for
};
});

View File

@ -20,7 +20,9 @@ class AddThis extends Component {
}
module.exports = cacheComponent(AddThis, 'share.addthis', props => {
const { share } = props;
return {
installUrl: props.install_url
installUrl: share.install_url
};
});

View File

@ -15,8 +15,10 @@ class ShareJs extends Component {
}
module.exports = cacheComponent(ShareJs, 'share.sharejs', props => {
const { helper } = props;
return {
cssUrl: props.cdn('social-share.js', '1.0.16', 'dist/css/share.min.css'),
jsUrl: props.cdn('social-share.js', '1.0.16', 'dist/js/social-share.min.js')
cssUrl: helper.cdn('social-share.js', '1.0.16', 'dist/css/share.min.css'),
jsUrl: helper.cdn('social-share.js', '1.0.16', 'dist/js/social-share.min.js')
};
});

View File

@ -20,7 +20,9 @@ class ShareThis extends Component {
}
module.exports = cacheComponent(ShareThis, 'share.sharethis', props => {
const { share } = props;
return {
installUrl: props.install_url
installUrl: share.install_url
};
});