-
+

@@ -30,25 +37,6 @@ class ImageDialog extends PureComponent {
);
}
-
- handleCloseClick = (evt) => {
- let { onClose } = this.props;
- if (onClose) {
- onClose({
- type: 'close',
- target: this,
- });
- }
- }
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- ImageDialog.propTypes = {
- imageURL: PropTypes.string,
- onClose: PropTypes.func,
- };
}
export {
diff --git a/src/widgets/media-view.jsx b/src/widgets/media-view.jsx
index 665f462..8f07071 100644
--- a/src/widgets/media-view.jsx
+++ b/src/widgets/media-view.jsx
@@ -1,38 +1,20 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
+import React from 'react';
-class MediaView extends PureComponent {
- static displayName = 'MediaView';
-
- render() {
- let { media, size } = this.props;
- let info = _.get(media, [ 'media_details', 'sizes', size ]);
- if (!info) {
- info = media;
- }
- let props = {
- src: info.source_url,
- width: info.width,
- height: info.height,
- };
- return
![]()
;
+function MediaView(props) {
+ const { media, size } = props;
+ let info = _.get(media, [ 'media_details', 'sizes', size ]);
+ if (!info) {
+ info = media;
}
+ return

;
}
MediaView.defaultProps = {
size: 'thumbnail',
};
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- MediaView.propTypes = {
- media: PropTypes.object,
- size: PropTypes.string,
- };
-}
-
export {
MediaView as default,
MediaView,
diff --git a/src/widgets/page-list-view.jsx b/src/widgets/page-list-view.jsx
index a23ad03..b491307 100644
--- a/src/widgets/page-list-view.jsx
+++ b/src/widgets/page-list-view.jsx
@@ -1,31 +1,17 @@
import _ from 'lodash';
-import React, { PureComponent } from 'react';
-import { Route } from 'routing';
+import React from 'react';
import HTML from 'widgets/html';
-class PageListView extends PureComponent {
- static displayName = 'PageListView';
-
- render() {
- let { route, page } = this.props;
- let title = _.get(page, 'title.rendered', '');
- let url = route.prefetchObjectURL(page);
- return (
-
- );
- }
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PageListView.propTypes = {
- page: PropTypes.object,
- route: PropTypes.instanceOf(Route).isRequired,
- };
+function PageListView(props) {
+ const { route, page } = props;
+ const title = _.get(page, 'title.rendered', '');
+ const url = route.prefetchObjectURL(page);
+ return (
+
+ );
}
export {
diff --git a/src/widgets/page-list.jsx b/src/widgets/page-list.jsx
index f27ae6b..eeae626 100644
--- a/src/widgets/page-list.jsx
+++ b/src/widgets/page-list.jsx
@@ -1,51 +1,32 @@
import _ from 'lodash';
-import React, { PureComponent } from 'react';
+import React, { useEffect } from 'react';
import { Route } from 'routing';
import PageListView from 'widgets/page-list-view';
-class PageList extends PureComponent {
- static displayName = 'PageList'
+function PageList(props) {
+ let { route, pages } = props;
+ if (!pages) {
+ return null;
+ }
- render() {
- let { route, pages } = this.props;
- if (!pages) {
- return null;
- }
+ useEffect(() => {
+ pages.more();
+ }, [ pages ]);
+
+ return (
+
+ {pages.map(renderPage)}
+
+ );
+
+ function renderPage(page, i) {
return (
-
- {
- pages.map((page) => {
- return (
- -
-
-
- );
- })
- }
-
- )
+
+
+
+ );
}
-
- componentDidMount() {
- this.componentDidUpdate();
- }
-
- componentDidUpdate(prevProps, prevState) {
- let { pages } = this.props;
- if (pages) {
- pages.more();
- }
- }
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PageList.propTypes = {
- pages: PropTypes.arrayOf(PropTypes.object),
- route: PropTypes.instanceOf(Route).isRequired,
- };
}
export {
diff --git a/src/widgets/page-view.jsx b/src/widgets/page-view.jsx
index 630b81f..b6d0fd6 100644
--- a/src/widgets/page-view.jsx
+++ b/src/widgets/page-view.jsx
@@ -1,41 +1,27 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
+import React from 'react';
import HTML from 'widgets/html';
-class PageView extends PureComponent {
- static displayName = 'PageView';
+function PageView(props) {
+ const { page, transform } = props;
+ const title = _.get(page, 'title.rendered', '');
+ const content = _.get(page, 'content.rendered', '');
+ const modified = _.get(page, 'modified_gmt');
+ const date = (modified) ? Moment(modified).format('LL') : '';
- render() {
- let { page, transform } = this.props;
- let title = _.get(page, 'title.rendered', '');
- let content = _.get(page, 'content.rendered', '');
- let date = _.get(page, 'modified_gmt');
- if (date) {
- date = Moment(date).format('LL');
- }
- return (
-
-
-
-
-
-
+ return (
+
+
- );
- }
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PageView.propTypes = {
- page: PropTypes.object,
- transform: PropTypes.func,
- };
+
+
+
+
+
+ );
}
export {
diff --git a/src/widgets/post-list-view.jsx b/src/widgets/post-list-view.jsx
index 1e2ac13..670dcae 100644
--- a/src/widgets/post-list-view.jsx
+++ b/src/widgets/post-list-view.jsx
@@ -1,46 +1,26 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
-import { Route } from 'routing';
+import React from 'react';
import HTML from 'widgets/html';
import MediaView from 'widgets/media-view';
-class PostListView extends PureComponent {
- static displayName = 'PostListView';
+function PostListView(props) {
+ const { route, post, media } = props;
+ const title = _.get(post, 'title.rendered', '');
+ const excerptRendered = _.get(post, 'excerpt.rendered', '');
+ const excerpt = cleanExcerpt(excerptRendered);
+ const url = route.prefetchObjectURL(post);
+ const published = _.get(post, 'date_gmt');
+ const date = (published) ? Moment(published).format('L') : '';
- render() {
- let { route, post, media } = this.props;
- let title = _.get(post, 'title.rendered', '');
- let excerpt = _.get(post, 'excerpt.rendered', '');
- excerpt = cleanExcerpt(excerpt);
- let url = route.prefetchObjectURL(post);
- let date = _.get(post, 'date_gmt');
- if (date) {
- date = Moment(date).format('L');
- }
- if (media) {
- return (
-
+ if (media) {
+ return (
+
+
+
- );
- } else {
- return (
-
+
@@ -51,28 +31,33 @@ class PostListView extends PureComponent {
- );
+
+ );
+ } else {
+ return (
+
+
+ );
+ }
+
+ function cleanExcerpt(excerpt) {
+ const index = excerpt.indexOf('
');
+ if (index !== -1) {
+ excerpt = excerpt.substr(0, index);
}
+ return excerpt;
}
}
-function cleanExcerpt(excerpt) {
- let index = excerpt.indexOf('
');
- if (index !== -1) {
- excerpt = excerpt.substr(0, index);
- }
- return excerpt;
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PostListView.propTypes = {
- post: PropTypes.object,
- route: PropTypes.instanceOf(Route).isRequired,
- };
-}
-
export {
PostListView as default,
PostListView,
diff --git a/src/widgets/post-list.jsx b/src/widgets/post-list.jsx
index b235df9..64929ac 100644
--- a/src/widgets/post-list.jsx
+++ b/src/widgets/post-list.jsx
@@ -1,62 +1,51 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
-import { Route } from 'routing';
+import React, { useEffect } from 'react';
import PostListView from 'widgets/post-list-view';
-class PostList extends PureComponent {
- static displayName = 'PostList'
+function PostList(props) {
+ const { route, posts, medias, minimum, maximum } = props;
- render() {
- let { route, posts, medias } = this.props;
- if (!posts) {
- return null;
- }
- return (
-
- {
- posts.map((post) => {
- let media = _.find(medias, { id: post.featured_media });
- return
- })
- }
-
- );
- }
+ useEffect(() => {
+ const handleScroll = (evt) => {
+ loadMore(0.5);
+ };
+ document.addEventListener('scroll', handleScroll);
- componentDidMount() {
- document.addEventListener('scroll', this.handleScroll);
- this.componentDidUpdate();
- }
-
- componentDidUpdate(prevProps, prevState) {
- let { posts, minimum, maximum } = this.props;
- if (posts && posts.length < minimum) {
+ return () => {
+ document.removeEventListener('scroll', handleScroll);
+ };
+ });
+ useEffect(() => {
+ if (posts && posts.more && posts.length < minimum) {
posts.more();
} else {
- // load more records if we're still near the bottom
- let { scrollTop, scrollHeight } = document.body.parentNode;
- if (scrollTop > scrollHeight * 0.75) {
- if (posts && posts.length < maximum) {
- posts.more();
- }
- }
+ loadMore(0.75);
}
+ }, [ posts ]);
+
+ if (!posts) {
+ return null;
+ }
+ return (
+
+ {posts.map(renderPost)}
+
+ );
+
+ function renderPost(post, i) {
+ let media = _.find(medias, { id: post.featured_media });
+ return
}
- componentWillUnmount() {
- document.removeEventListener('scroll', this.handleScroll);
- }
-
- handleScroll = (evt) => {
- let { posts, maximum } = this.props;
- let { scrollTop, scrollHeight } = document.body.parentNode;
- if (scrollTop > scrollHeight * 0.5) {
- if (posts && posts.length < maximum) {
+ function loadMore(fraction) {
+ const { scrollTop, scrollHeight } = document.body.parentNode;
+ if (scrollTop > scrollHeight * fraction) {
+ if (posts && posts.more && posts.length < maximum) {
posts.more();
}
- }
+ }
}
}
@@ -65,18 +54,6 @@ PostList.defaultProps = {
maximum: 500,
};
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PostList.propTypes = {
- posts: PropTypes.arrayOf(PropTypes.object),
- medias: PropTypes.arrayOf(PropTypes.object),
- route: PropTypes.instanceOf(Route),
- minimum: PropTypes.number,
- maximum: PropTypes.number,
- };
-}
-
export {
PostList as default,
PostList,
diff --git a/src/widgets/post-view.jsx b/src/widgets/post-view.jsx
index ef2d413..846d672 100644
--- a/src/widgets/post-view.jsx
+++ b/src/widgets/post-view.jsx
@@ -1,52 +1,17 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
+import React, { useState } from 'react';
import HTML from 'widgets/html';
import ImageDialog from 'widgets/image-dialog';
-class PostView extends PureComponent {
- static displayName = 'PostView';
+function PostView(props) {
+ const { post, author, transform } = props;
+ const [ imageURL, setImageURL ] = useState(null);
- constructor(props) {
- super(props);
- this.state = {
- imageURL: null,
- };
- }
-
- render() {
- let { post, author, transform } = this.props;
- let title = _.get(post, 'title.rendered', '');
- let content = _.get(post, 'content.rendered', '');
- let date = _.get(post, 'date_gmt');
- let name = _.get(author, 'name', '\u00a0');
- if (date) {
- date = Moment(date).format('LL');
- }
- return (
-
- {this.renderImageDialog()}
-
- );
- }
-
- renderImageDialog() {
- let { imageURL } = this.state;
- return
;
- }
-
- handleClick = (evt) => {
- let target = evt.target;
- let container = evt.currentTarget;
+ const handleClick = (evt) => {
+ const target = evt.target;
+ const container = evt.currentTarget;
if (target.tagName === 'IMG') {
let link;
for (let p = target; p && p !== container; p = p.parentNode) {
@@ -56,26 +21,33 @@ class PostView extends PureComponent {
}
}
if (link) {
- let imageURL = link.href;
- this.setState({ imageURL });
+ setImageURL(link.href);
evt.preventDefault();
}
}
- }
-
- handleDialogClose = (evt) => {
- this.setState({ imageURL: null });
- }
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- PostView.propTypes = {
- post: PropTypes.object,
- author: PropTypes.object,
- transform: PropTypes.func,
};
+ const handleDialogClose = (evt) => {
+ setImageURL(null);
+ };
+
+ const title = _.get(post, 'title.rendered', '');
+ const content = _.get(post, 'content.rendered', '');
+ const name = _.get(author, 'name', '\u00a0');
+ const published = _.get(post, 'date_gmt');
+ const date = (published) ? Moment(published).format('LL') : '';
+ return (
+
+
+
+ );
}
export {
diff --git a/src/widgets/side-nav.jsx b/src/widgets/side-nav.jsx
index 8e26b40..94c898e 100644
--- a/src/widgets/side-nav.jsx
+++ b/src/widgets/side-nav.jsx
@@ -1,160 +1,134 @@
import _ from 'lodash';
import Moment from 'moment';
-import React, { PureComponent } from 'react';
-import { AsyncComponent } from 'relaks';
-import { Route } from 'routing';
-import WordPress from 'wordpress';
+import React, { useState } from 'react';
+import Relaks, { useProgress } from 'relaks/hooks';
-class SideNav extends AsyncComponent {
- static displayName = 'SideNav';
+async function SideNav(props) {
+ const { wp, route } = props;
+ const { date } = route.params;
+ const [ selectedYear, setSelectedYear ] = useState(() => {
+ return _.get(date, 'year', Moment().year());
+ });
+ const [ show ] = useProgress(50, 50);
- constructor(props) {
- super(props);
- let { route } = this.props;
- let { date } = route.params;
- let selectedYear = _.get(date, 'year', Moment().year());
- this.state = { selectedYear };
- }
-
- async renderAsync(meanwhile) {
- let { wp, route } = this.props;
- let { selectedYear } = this.state;
- let props = {
- route,
- selectedYear,
- onYearSelect: this.handleYearSelect,
- };
- meanwhile.delay(50, 50);
- meanwhile.show(
);
-
- // get all categories
- props.categories = await wp.fetchCategories();
- meanwhile.show(
);
-
- // get top tags
- props.tags = await wp.fetchTopTags();
- meanwhile.show(
);
-
- // get the date range of posts and use that to build the list of
- // years and months
- let range = await wp.getPostDateRange();
- props.archives = [];
- if (range) {
- // loop through the years
- let lastYear = range.latest.year();
- let firstYear = range.earliest.year();
- for (let y = lastYear; y >= firstYear; y--) {
- let yearEntry = {
- year: y,
- label: Moment(`${y}-01-01`).format('YYYY'),
- months: []
- };
- props.archives.push(yearEntry);
-
- // loop through the months
- let lastMonth = (y === lastYear) ? range.latest.month() : 11;
- let firstMonth = (y === firstYear) ? range.earliest.month() : 0;
- for (let m = lastMonth; m >= firstMonth; m--) {
- let start = Moment(new Date(y, m, 1));
- let end = start.clone().endOf('month');
- let monthEntry = {
- year: y,
- month: m + 1,
- label: start.format('MMMM'),
- };
- yearEntry.months.push(monthEntry);
- }
- }
- meanwhile.show(
);
- }
-
- if (!wp.ssr) {
- props.postLists = [];
- try {
- // load the posts of each month of the selected year
- for (let yearEntry of props.archives) {
- if (yearEntry.year === selectedYear) {
- for (let monthEntry of yearEntry.months) {
- let posts = await wp.fetchPostsInMonth(monthEntry);
- props.postLists = _.concat(props.postLists, { monthEntry, posts });
- meanwhile.show(
);
- }
- }
- }
-
- // load the posts of each category
- for (let category of props.categories) {
- if (category.count > 0) {
- let posts = await wp.fetchPostsInCategory(category);
- props.postLists = _.concat(props.postLists, { category, posts });
- meanwhile.show(
);
- }
- }
-
- // load the posts of each tag
- for (let tag of props.tags) {
- if (tag.count > 0) {
- let posts = await wp.fetchPostsWithTag(tag);
- props.postLists = _.concat(props.postLists, { tag, posts });
- meanwhile.show(
);
- }
- }
- } catch (err) {
- }
- }
- return
;
- }
-
- handleYearSelect = (evt) => {
- let { selectedYear } = this.state;
- if (selectedYear !== evt.year) {
- selectedYear = evt.year;
+ const handleYearClick = (evt) => {
+ const year = parseInt(evt.currentTarget.getAttribute('data-year'));
+ if (selectedYear !== year) {
+ setSelectedYear(year);
} else {
- selectedYear = NaN;
+ setSelectedYear(NaN);
}
- this.setState({ selectedYear });
+ };
+ const handleMoreTagClick = (evt) => {
+ tags.more();
+ };
+
+ render();
+ // get all categories
+ const categories = await wp.fetchCategories();
+ render ();
+ // get top tags
+ const tags = await wp.fetchTopTags();
+ render ();
+
+ // get the date range of posts and use that to build the list of
+ // years and months
+ const range = await wp.getPostDateRange();
+ const archives = [];
+ if (range) {
+ // loop through the years
+ let lastYear = range.latest.year();
+ let firstYear = range.earliest.year();
+ for (let y = lastYear; y >= firstYear; y--) {
+ let yearEntry = {
+ year: y,
+ label: Moment(`${y}-01-01`).format('YYYY'),
+ months: []
+ };
+ archives.push(yearEntry);
+
+ // loop through the months
+ let lastMonth = (y === lastYear) ? range.latest.month() : 11;
+ let firstMonth = (y === firstYear) ? range.earliest.month() : 0;
+ for (let m = lastMonth; m >= firstMonth; m--) {
+ let start = Moment(new Date(y, m, 1));
+ let end = start.clone().endOf('month');
+ let monthEntry = {
+ year: y,
+ month: m + 1,
+ label: start.format('MMMM'),
+ };
+ yearEntry.months.push(monthEntry);
+ }
+ }
+ render();
}
-}
-class SideNavSync extends PureComponent {
- static displayName = 'SideNavSync';
+ const postLists = [];
+ if (!wp.ssr) {
+ try {
+ // load the posts of each month of the selected year
+ for (let yearEntry of archives) {
+ if (yearEntry.year === selectedYear) {
+ for (let monthEntry of yearEntry.months) {
+ const posts = await wp.fetchPostsInMonth(monthEntry);
+ postLists.push({ monthEntry, posts });
+ render();
+ }
+ }
+ }
- render() {
- return (
+ // load the posts of each category
+ for (let category of categories) {
+ if (category.count > 0) {
+ const posts = await wp.fetchPostsInCategory(category);
+ postLists.push({ category, posts });
+ render();
+ }
+ }
+
+ // load the posts of each tag
+ for (let tag of tags) {
+ if (tag.count > 0) {
+ const posts = await wp.fetchPostsWithTag(tag);
+ postLists.push({ tag, posts });
+ render();
+ }
+ }
+ } catch (err) {
+ }
+ }
+
+ function render() {
+ show(
- {this.renderCategories()}
- {this.renderTags()}
- {this.renderArchives()}
+ {renderCategories()}
+ {renderTags()}
+ {renderArchives()}
- )
+ );
}
- renderCategories() {
- let { categories } = this.props;
+ function renderCategories() {
// only top-level categories
- categories = _.filter(categories, { parent: 0 });
+ const subcategories = _.filter(categories, { parent: 0 });
// don't show categories with no post
- categories = _.filter(categories, 'count');
- categories = _.orderBy(categories, [ 'name' ], [ 'asc' ]);
- if (_.isEmpty(categories)) {
+ const filtered = _.filter(subcategories, 'count');
+ const ordered = _.orderBy(filtered, [ 'name' ], [ 'asc' ]);
+ if (_.isEmpty(ordered)) {
return null;
}
return (
Categories
- {
- categories.map((category, i) => {
- return this.renderCategory(category, i);
- })
- }
+ {ordered.map(renderCategory)}
);
}
- renderCategory(category, i) {
- let { route, postLists } = this.props;
+ function renderCategory(category, i) {
let { categorySlug } = route.params;
let name = _.get(category, 'name', '');
let description = _.unescape(_.get(category, 'description', '').replace(/'/g, "'"));
@@ -171,37 +145,31 @@ class SideNavSync extends PureComponent {
return (
{name}
- {this.renderSubcategories(category)}
+ {renderSubcategories(category)}
);
}
- renderTags() {
- let { tags } = this.props;
+ function renderTags() {
// don't show tags with no post
- tags = _.filter(tags, 'count');
+ const activeTags = _.filter(tags, 'count');
// list tags with more posts first
- tags = _.orderBy(tags, [ 'count', 'name' ], [ 'desc', 'asc' ]);
- if (_.isEmpty(tags)) {
+ const ordered = _.orderBy(activeTags, [ 'count', 'name' ], [ 'desc', 'asc' ]);
+ if (_.isEmpty(ordered)) {
return null;
}
return (
Tags
- {
- tags.map((tag, i) => {
- return this.renderTag(tag, i);
- })
- }
- {this.renderMoreTagButton()}
+ {ordered.map(renderTag)}
+ {renderMoreTagButton()}
);
}
- renderTag(tag, i) {
- let { route, postLists } = this.props;
+ function renderTag(tag, i) {
let { tagSlug } = route.params;
let name = _.get(tag, 'name', '');
let description = _.unescape(_.get(tag, 'description', '').replace(/'/g, "'"));
@@ -223,38 +191,31 @@ class SideNavSync extends PureComponent {
);
}
- renderMoreTagButton() {
- let { tags } = this.props;
+ function renderMoreTagButton() {
if (!_.some(tags, 'count')) {
return null;
}
if (!(tags.length < tags.total) || tags.length >= 100) {
return null;
}
- return
... more;
+ return
... more;
}
- renderSubcategories(category) {
- let { categories } = this.props;
- let subcategories = _.filter(categories, { parent: category.id });
- subcategories = _.filter(subcategories, 'count');
- subcategories = _.orderBy(subcategories, [ 'count', 'name' ], [ 'desc', 'asc' ]);
- if (_.isEmpty(subcategories)) {
+ function renderSubcategories(category) {
+ const subcategories = _.filter(categories, { parent: category.id });
+ const filtered = _.filter(subcategories, 'count');
+ const ordered = _.orderBy(filtered, [ 'count', 'name' ], [ 'desc', 'asc' ]);
+ if (_.isEmpty(ordered)) {
return null;
}
return (
- {
- subcategories.map((subcategory, i) => {
- return this.renderCategory(subcategory, i);
- })
- }
+ {ordered.map(renderCategory)}
);
}
- renderArchives() {
- let { archives } = this.props;
+ function renderArchives() {
if (_.isEmpty(archives)) {
return null;
}
@@ -262,109 +223,68 @@ class SideNavSync extends PureComponent {
Archives
- {
- archives.map((yearEntry, i) => {
- return this.renderYear(yearEntry, i);
- })
- }
+ {archives.map(renderYear)}
);
}
- renderYear(yearEntry, i) {
- let { selectedYear } = this.props;
- let listClass = 'months';
+ function renderYear(yearEntry, i) {
+ const listClassNames = [ 'months'] ;
if (yearEntry.year !== selectedYear) {
- listClass += ' collapsed';
+ listClassNames.push('collapsed');
}
return (
-
+
{yearEntry.label}
-
- {
- yearEntry.months.map((entry, i) => {
- return this.renderMonth(entry, i);
- })
- }
+
+ {yearEntry.months.map(renderMonth)}
)
}
- renderMonth(monthEntry, i) {
- let { route, postLists, selectedYear } = this.props;
- let { date } = route.params;
- let className, url;
+ function renderMonth(monthEntry, i) {
+ const { date } = route.params;
+ const classNames = [];
+ let url;
if (monthEntry.year !== selectedYear) {
return null;
}
if (date && monthEntry.year === date.year && monthEntry.month === date.month) {
- className = 'selected';
+ classNames.push('selected');
}
- let postList = _.find(postLists, { monthEntry });
+ const postList = _.find(postLists, { monthEntry });
if (!postList || !_.isEmpty(postList.posts)) {
url = route.prefetchArchiveURL(monthEntry);
} else {
- className = 'disabled';
+ classNames.push('disabled');
}
return (
- {monthEntry.label}
+
+ {monthEntry.label}
+
);
}
- handleYearClick = (evt) => {
- let { onYearSelect } = this.props;
- let year = parseInt(evt.currentTarget.getAttribute('data-year'));
- if (onYearSelect) {
- onYearSelect({
- type: 'yearselect',
- target: this,
- year,
- });
+ function hasRecentPost(postList, daysOld) {
+ if (!postList || _.isEmpty(postList.posts)) {
+ return false;
}
- }
-
- handleMoreTagClick = (evt) => {
- let { tags } = this.props;
- tags.more();
+ const post = _.first(postList.posts);
+ const limit = Moment().subtract(daysOld, 'day');
+ const publicationDate = Moment(post.date_gmt);
+ return limit < publicationDate;
}
}
-function hasRecentPost(postList, daysOld) {
- if (!postList || _.isEmpty(postList.posts)) {
- return false;
- }
- let post = _.first(postList.posts);
- let limit = Moment().subtract(daysOld, 'day');
- let publicationDate = Moment(post.date_gmt);
- return limit < publicationDate;
-}
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- SideNav.propTypes = {
- wp: PropTypes.instanceOf(WordPress).isRequired,
- route: PropTypes.instanceOf(Route).isRequired,
- };
- SideNavSync.propTypes = {
- categories: PropTypes.arrayOf(PropTypes.object),
- tags: PropTypes.arrayOf(PropTypes.object),
- archives: PropTypes.arrayOf(PropTypes.object),
- postLists: PropTypes.arrayOf(PropTypes.object),
- selectedYear: PropTypes.number,
- route: PropTypes.instanceOf(Route),
- onYearSelect: PropTypes.func,
- };
-}
+const component = Relaks(SideNav);
export {
- SideNav as default,
- SideNav,
- SideNavSync,
+ component as default,
+ component as SideNav,
};
diff --git a/src/widgets/tag-list.jsx b/src/widgets/tag-list.jsx
index 8a1240f..69c4b3e 100644
--- a/src/widgets/tag-list.jsx
+++ b/src/widgets/tag-list.jsx
@@ -1,30 +1,20 @@
import _ from 'lodash';
-import React, { PureComponent } from 'react';
+import React from 'react';
-class TagList extends PureComponent {
- static displayName = 'TagList';
-
- render() {
- let { tags } = this.props;
- if (_.isEmpty(tags)) {
- return null;
- }
- return (
-
- Tags:
- {
- tags.map((tag, i) => {
- return this.renderTag(tag, i);
- })
- }
-
- );
+function TagList(props) {
+ const { route, tags } = props;
+ if (_.isEmpty(tags)) {
+ return null;
}
+ return (
+
+ Tags: {tags.map(renderTag)}
+
+ );
- renderTag(tag, i) {
- let { route } = this.props;
- let name = _.get(tag, 'name', '');
- let url = route.prefetchObjectURL(tag);
+ function renderTag(tag, i) {
+ const name = _.get(tag, 'name', '');
+ const url = route.prefetchObjectURL(tag);
return (
{name}
@@ -34,14 +24,6 @@ class TagList extends PureComponent {
}
}
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- TagList.propTypes = {
- tags: PropTypes.arrayOf(PropTypes.object),
- };
-}
-
export {
TagList as default,
TagList,
diff --git a/src/widgets/top-nav.jsx b/src/widgets/top-nav.jsx
index 63fab2c..8a920ea 100644
--- a/src/widgets/top-nav.jsx
+++ b/src/widgets/top-nav.jsx
@@ -1,52 +1,46 @@
import _ from 'lodash';
-import React, { PureComponent } from 'react';
-import { AsyncComponent } from 'relaks';
-import { Route } from 'routing';
-import WordPress from 'wordpress';
+import React from 'react';
+import Relaks, { useProgress, useSaveBuffer } from 'relaks/hooks';
-class TopNav extends AsyncComponent {
- static displayName = 'TopNav';
+async function TopNav(props) {
+ const { wp, route } = props;
+ const [ show ] = useProgress();
+ const [ search, setSearch ] = useSaveBuffer(route.params.search, {
+ delay: 500,
+ save: (newSearch) => {
+ const url = route.getSearchURL(newSearch);
+ const options = {
+ replace: (route.params.pageType === 'search')
+ };
+ route.change(url);
+ },
+ });
- async renderAsync(meanwhile) {
- let { wp, route } = this.props;
- let props = {
- route,
- };
- meanwhile.show();
- props.site = await wp.fetchSite();
- meanwhile.show();
- props.pages = await wp.fetchPages();
- return ;
- }
-}
+ const handleSearchChange = (evt) => {
+ setSearch(evt.target.value);
+ };
-class TopNavSync extends PureComponent {
- static displayName = 'TopNavSync';
+ render();
+ const site = await wp.fetchSite();
+ render();
+ const pages = await wp.fetchPages();
+ render();
- constructor(props) {
- super(props);
- let { route } = props;
- let { search } = route.params;
- this.searchTimeout = 0;
- this.state = { search };
- }
-
- render() {
- let { onMouseOver, onMouseOut } = this.props;
- return (
-
- {this.renderTitleBar()}
- {this.renderPageLinkBar()}
- {this.renderSearchBar()}
+ function render() {
+ show(
+
+ {renderTitleBar()}
+ {renderPageLinkBar()}
+ {renderSearchBar()}
);
}
- renderTitleBar() {
- let { route, site } = this.props;
- let name = _.get(site, 'name', '');
- let description = _.unescape(_.get(site, 'description', '').replace(/'/g, "'"));
- let url = route.getRootURL();
+ function renderTitleBar() {
+ const name = _.get(site, 'name', '');
+ const descriptionHTML = _.get(site, 'description', '');
+ const description = _.unescape(descriptionHTML.replace(/'/g, "'"));
+ const url = route.getRootURL();
return (
@@ -59,25 +53,19 @@ class TopNavSync extends PureComponent {
);
}
- renderPageLinkBar() {
- let { pages } = this.props;
- pages = _.filter(pages, { parent: 0 });
- pages = _.sortBy(pages, 'menu_order');
+ function renderPageLinkBar() {
+ let filtered = _.filter(pages, { parent: 0 });
+ let ordered = _.sortBy(filtered, 'menu_order');
return (
- {
- pages.map((page, i) => {
- return this.renderPageLinkButton(page, i);
- })
- }
+ {ordered.map(renderPageLinkButton)}
);
}
- renderPageLinkButton(page, i) {
- let { route } = this.props;
- let title = _.get(page, 'title.rendered');
- let url = route.prefetchObjectURL(page);
+ function renderPageLinkButton(page, i) {
+ const title = _.get(page, 'title.rendered');
+ const url = route.prefetchObjectURL(page);
return (
{title}
@@ -85,71 +73,21 @@ class TopNavSync extends PureComponent {
);
}
- renderSearchBar() {
- let { route } = this.props;
- let { search } = this.state;
+ function renderSearchBar() {
return (
-
+
);
}
-
- performSearch = (evt) => {
- let { search } = this.state;
- let { route } = this.props;
- let url = route.getSearchURL(search);
- let options = {
- replace: (route.params.pageType === 'search')
- };
- route.change(url);
- }
-
- componentDidUpdate(prevProps, prevState) {
- let { route } = this.props;
- if (prevProps.route !== route) {
- let { search } = route.params;
- this.setState({ search });
- }
- }
-
- componentWillUnmount() {
- clearTimeout(this.searchTimeout);
- }
-
- handleSearchChange = (evt) => {
- let search = evt.target.value;
- this.setState({ search });
- clearTimeout(this.searchTimeout);
- this.searchTimeout = setTimeout(this.performSearch, 500);
- }
}
-TopNavSync.defaultProps = {
- site: {},
- pages: [],
- search: '',
-};
-
-if (process.env.NODE_ENV !== 'production') {
- const PropTypes = require('prop-types');
-
- TopNav.propTypes = {
- wp: PropTypes.instanceOf(WordPress).isRequired,
- route: PropTypes.instanceOf(Route).isRequired,
- };
- TopNavSync.propTypes = {
- site: PropTypes.object,
- pages: PropTypes.arrayOf(PropTypes.object),
- route: PropTypes.instanceOf(Route).isRequired,
- };
-}
+const component = Relaks(TopNav);
export {
- TopNav as default,
- TopNav,
- TopNavSync,
+ component as default,
+ component as TopNav,
};
diff --git a/webpack.resolve.js b/webpack.resolve.js
index d229340..03273fd 100644
--- a/webpack.resolve.js
+++ b/webpack.resolve.js
@@ -14,7 +14,8 @@ module.exports = function(config) {
})
}
}
- })
+ });
+ config.resolve.modules.push(Path.resolve('./node_modules'));
};
function resolve(type, module) {