diff --git a/server/docker-compose.yml b/server/docker-compose.yml
index 633510a..150ad1e 100644
--- a/server/docker-compose.yml
+++ b/server/docker-compose.yml
@@ -24,11 +24,9 @@ services:
WORDPRESS_DB_PASSWORD: wordpress
restart: always
node:
- image: node:8
+ image: wordpress-example
volumes:
- - ..:/opt/example
- web_cache:/var/cache/nginx
- command: [ node, /opt/example/server/index.js ]
environment:
WORDPRESS_HOST: wordpress
NGINX_HOST: nginx
diff --git a/src/pages/archive-page.jsx b/src/pages/archive-page.jsx
index 9d14fe2..eeb4938 100644
--- a/src/pages/archive-page.jsx
+++ b/src/pages/archive-page.jsx
@@ -14,10 +14,7 @@ class ArchivePage extends AsyncComponent {
let { wp, route } = this.props;
let { date } = route.params;
let month = Moment(`${date.year}-${_.padStart(date.month, 2, '0')}`);
- let props = {
- route,
- month,
- };
+ let props = { route, month };
meanwhile.show();
let after = month.toISOString();
let before = month.clone().endOf('month').toISOString();
@@ -33,7 +30,7 @@ class ArchivePageSync extends PureComponent {
render() {
let { route, posts, month } = this.props;
let monthLabel = month.format('MMMM YYYY');
- let trail = [ { label: 'Archive' }, { label: monthLabel } ];
+ let trail = [ { label: 'Archives' }, { label: monthLabel } ];
return (
diff --git a/src/pages/category-page.jsx b/src/pages/category-page.jsx
index b24f0a8..2e61730 100644
--- a/src/pages/category-page.jsx
+++ b/src/pages/category-page.jsx
@@ -12,15 +12,22 @@ class CategoryPage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
- let { categorySlugs } = route.params;
- let props = {
- route,
- };
+ let { categorySlug } = route.params;
+ let props = { route };
meanwhile.show(
);
- props.categories = await wp.fetchMultiple('/wp/v2/categories/', categorySlugs);
+ props.category = await wp.fetchOne('/wp/v2/categories/', categorySlug);
+ props.parentCategories = [];
+ let parentID = props.category.parent;
+ while (parentID) {
+ let parentCategory = await wp.fetchOne('/wp/v2/categories/', parentID);
+ if (!parentCategory) {
+ break;
+ }
+ props.parentCategories.push(parentCategory);
+ parentID = parentCategory.parent;
+ }
meanwhile.show(
);
- let category = _.last(props.categories);
- props.posts = await wp.fetchList(`/wp/v2/posts/?categories=${category.id}`);
+ props.posts = await wp.fetchList(`/wp/v2/posts/?categories=${props.category.id}`);
return
;
}
}
@@ -29,18 +36,15 @@ class CategoryPageSync extends PureComponent {
static displayName = 'CategoryPageSync';
render() {
- let { route, posts, categories } = this.props;
+ let { route, posts, category, parentCategories } = this.props;
let trail = [ { label: 'Categories' } ];
- let category = _.last(categories);
- for (let c of categories) {
- let label = _.get(c, 'name', '');
- if (c !== category) {
- let url = route.getObjectURL(c);
- trail.push({ label, url });
- } else {
- trail.push({ label });
- }
+ let categoryLabel = _.get(category, 'name', '');
+ for (let parentCategory of parentCategories) {
+ let label = _.get(parentCategory, 'name', '');
+ let url = route.getObjectURL(parentCategory);
+ trail.push({ label, url });
}
+ trail.push({ label: categoryLabel });
return (
@@ -59,6 +63,7 @@ if (process.env.NODE_ENV !== 'production') {
};
CategoryPageSync.propTypes = {
category: PropTypes.object,
+ parentCategories: PropTypes.arrayOf(PropTypes.object),
posts: PropTypes.arrayOf(PropTypes.object),
route: PropTypes.instanceOf(Route),
};
diff --git a/src/pages/page-page.jsx b/src/pages/page-page.jsx
index 2a5ea2e..903a2c8 100644
--- a/src/pages/page-page.jsx
+++ b/src/pages/page-page.jsx
@@ -14,15 +14,21 @@ class PagePage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
- let { pageSlugs } = route.params;
- let props = {
- route,
- };
+ let { pageSlug } = route.params;
+ let props = { route };
meanwhile.show(
);
- props.pages = await wp.fetchMultiple('/wp/v2/pages/', pageSlugs);
- meanwhile.show(
);
- let page = _.last(props.pages);
- props.childPages = await wp.fetchList(`/wp/v2/pages/?parent=${page.id}`, { minimum: '100%' });
+ props.page = await wp.fetchOne('/wp/v2/pages/', pageSlug);
+ props.parentPages = [];
+ let parentID = props.page.parent;
+ while (parentID) {
+ let parentPage = await wp.fetchOne('/wp/v2/pages/', parentID);
+ if (!parentPage) {
+ break;
+ }
+ props.parentPages.push(parentPage);
+ parentID = parentPage.parent;
+ }
+ props.childPages = await wp.fetchList(`/wp/v2/pages/?parent=${props.page.id}`);
return
;
}
}
@@ -31,15 +37,12 @@ class PagePageSync extends PureComponent {
static displayName = 'PagePageSync';
render() {
- let { route, pages, childPages, transform } = this.props;
- let page = _.last(pages);
+ let { route, page, parentPages, childPages, transform } = this.props;
let trail = [];
- for (let p of pages) {
- if (p !== page) {
- let title = _.get(page, 'title.rendered', '');
- let url = route.getObjectURL(p);
- trail.push({ label:
, url })
- }
+ for (let parentPage of parentPages) {
+ let title = _.get(parentPage, 'title.rendered', '');
+ let url = route.getObjectURL(parentPage);
+ trail.push({ label:
, url })
}
return (
diff --git a/src/pages/post-page.jsx b/src/pages/post-page.jsx
index b1bf4ea..de1f00d 100644
--- a/src/pages/post-page.jsx
+++ b/src/pages/post-page.jsx
@@ -15,14 +15,11 @@ class PostPage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
let { postSlug } = route.params;
- let props = {
- route,
- };
+ let props = { route };
meanwhile.show(
);
props.post = await wp.fetchOne('/wp/v2/posts/', postSlug);
meanwhile.show(
);
- let allCategories = await wp.fetchList('/wp/v2/categories/', { minimum: '100%' });
- props.categories = findMatchingCategories(allCategories, props.post.categories, route.history);
+ props.categories = await this.findCategoryChain(props.post.categories);
meanwhile.show(
);
props.author = await wp.fetchOne('/wp/v2/users/', props.post.author);
if (!wp.ssr) {
@@ -31,6 +28,64 @@ class PostPage extends AsyncComponent {
}
return
;
}
+
+ async findCategoryChain(ids) {
+ let { wp, route } = this.props;
+ let allCategories = await wp.fetchList('/wp/v2/categories/', { minimum: '100%' });
+
+ // add categories, including their parents as well
+ let applicable = [];
+ let include = (id) => {
+ let category = _.find(allCategories, { id })
+ if (category) {
+ if (!_.includes(applicable, category)) {
+ applicable.push(category);
+ }
+ // add parent category as well
+ include(category.parent);
+ }
+ };
+ for (let id of ids) {
+ include(id);
+ }
+
+ // see how recently a category was visited
+ let historyIndex = (c) => {
+
+ return _.findLastIndex(route.history, { params: { categorySlug: c.slug }});
+ };
+ // see how deep a category is
+ let depth = (c) => {
+ if (c.parent) {
+ let parent = _.find(allCategories, { id: c.parent });
+ if (parent) {
+ return depth(parent) + 1;
+ }
+ }
+ return 0;
+ };
+
+ // order applicable categories based on how recently it was visited,
+ // how deep it is, and alphabetically; the first criteria makes our
+ // breadcrumb works more sensibly
+ applicable = _.orderBy(applicable, [ historyIndex, depth, 'name' ], [ 'desc', 'desc', 'asc' ]);
+ let anchorCategory = _.first(applicable);
+
+ let trail = [];
+ if (anchorCategory) {
+ // add category and its ancestors
+ for (let c = anchorCategory; c; c = _.find(applicable, { id: c.parent })) {
+ trail.unshift(c);
+ }
+ // add applicable child categories
+ for (let c = anchorCategory; c; c = _.find(applicable, { parent: c.id })) {
+ if (c !== anchorCategory) {
+ trail.push(c);
+ }
+ }
+ }
+ return trail;
+ }
}
class PostPageSync extends PureComponent {
@@ -54,61 +109,6 @@ class PostPageSync extends PureComponent {
}
}
-function includeCategory(list, id, allCategories) {
- let category = _.find(allCategories, { id })
- if (category) {
- if (!_.includes(list, category)) {
- list.push(category);
- }
- // add parent category as well
- includeCategory(list, category.parent, allCategories);
- }
-}
-
-function findMatchingCategories(allCategories, ids, history) {
- let applicable = [];
- for (let id of ids) {
- includeCategory(applicable, id, allCategories);
- }
-
- let historyIndex = (c) => {
- return _.findLastIndex(history, (route) => {
- if (route.params.categorySlugs) {
- return _.includes(route.params.categorySlugs, c.slug);
- }
- });
- };
- let depth = (c) => {
- if (c.parent) {
- let parent = _.find(allCategories, { id: c.parent });
- if (parent) {
- return depth(parent) + 1;
- }
- }
- return 0;
- };
-
- // order applicable categories based on how recently it was visited,
- // how deep it is, and alphabetically
- applicable = _.orderBy(applicable, [ historyIndex, depth, 'name' ], [ 'desc', 'desc', 'asc' ]);
- let anchorCategory = _.first(applicable);
-
- let trail = [];
- if (anchorCategory) {
- // add category and its ancestors
- for (let c = anchorCategory; c; c = _.find(applicable, { id: c.parent })) {
- trail.unshift(c);
- }
- // add applicable child categories
- for (let c = anchorCategory; c; c = _.find(applicable, { parent: c.id })) {
- if (c !== anchorCategory) {
- trail.push(c);
- }
- }
- }
- return trail;
-}
-
if (process.env.NODE_ENV !== 'production') {
const PropTypes = require('prop-types');
diff --git a/src/pages/search-page.jsx b/src/pages/search-page.jsx
index 127cace..affcde0 100644
--- a/src/pages/search-page.jsx
+++ b/src/pages/search-page.jsx
@@ -12,14 +12,12 @@ class SearchPage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
let { search } = route.params;
- let props = {
- route,
- };
+ let props = { route };
meanwhile.show(
);
props.categories = await wp.fetchList('/wp/v2/categories/');
if (search) {
- let url = `/wp/v2/posts/?search=${encodeURIComponent(search)}'`;
- props.posts = await wp.fetchList(url);
+ let s = encodeURIComponent(search);
+ props.posts = await wp.fetchList(`/wp/v2/posts/?search=${s}`);
} else {
props.posts = null;
}
diff --git a/src/pages/tag-page.jsx b/src/pages/tag-page.jsx
index 3491b5c..e921efd 100644
--- a/src/pages/tag-page.jsx
+++ b/src/pages/tag-page.jsx
@@ -13,9 +13,7 @@ class TagPage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
let { tagSlug } = route.params;
- let props = {
- route,
- };
+ let props = { route };
meanwhile.show(
);
props.tag = await wp.fetchOne('/wp/v2/tags/', tagSlug);
meanwhile.show(
);
diff --git a/src/pages/welcome-page.jsx b/src/pages/welcome-page.jsx
index 0f67613..329ba6a 100644
--- a/src/pages/welcome-page.jsx
+++ b/src/pages/welcome-page.jsx
@@ -10,11 +10,8 @@ class WelcomePage extends AsyncComponent {
async renderAsync(meanwhile) {
let { wp, route } = this.props;
- let props = {
- route,
- };
+ let props = { route };
meanwhile.show(
);
- props.categories = await wp.fetchList('/wp/v2/categories/');
props.posts = await wp.fetchList('/wp/v2/posts/');
return
;
}
@@ -24,10 +21,10 @@ class WelcomePageSync extends PureComponent {
static displayName = 'WelcomePageSync';
render() {
- let { route, categories, posts } = this.props;
+ let { route, posts } = this.props;
return (
);
}
diff --git a/src/routing.js b/src/routing.js
index 495582d..2e03b1b 100644
--- a/src/routing.js
+++ b/src/routing.js
@@ -61,7 +61,7 @@ class Route {
// get the site URL and see what the page's URL would be if it
// were on WordPress itself
let siteURL = await this.getSiteURL();
- let link = _.trimEnd(siteURL + path, '/') + '/';
+ let link = _.trimEnd(siteURL + path, '/');
// see if it's a search
let search = query.s;
@@ -85,31 +85,29 @@ class Route {
if (postID) {
let post = await this.dataSource.fetchOne('/wp/v2/posts/', postID);
if (post) {
- let postSlug = post.slug;
- return { pageType: 'post', postSlug, siteURL };
+ return { pageType: 'post', postSlug: post.slug, siteURL };
}
}
// see if it's pointing to a page
let allPages = await this.dataSource.fetchList('/wp/v2/pages/', { minimum: '100%' });
- let pageSlugs = findMatchingSlugs(allPages, link);
- if (pageSlugs) {
- return { pageType: 'page', pageSlugs, siteURL };
+ let page = _.find(allPages, { link });
+ if (page) {
+ return { pageType: 'page', pageSlug: page.slug, siteURL };
}
// see if it's pointing to a category
let allCategories = await this.dataSource.fetchList('/wp/v2/categories/', { minimum: '100%' });
- let categorySlugs = findMatchingSlugs(allCategories, link);
- if (categorySlugs) {
- return { pageType: 'category', categorySlugs, siteURL };
+ let category = _.find(allCategories, { link });
+ if (category) {
+ return { pageType: 'category', categorySlug: category.slug, siteURL };
}
// see if it's pointing to a tag
let allTags = await this.dataSource.fetchList('/wp/v2/tags/', { minimum: '100%' });
- let tagSlugs = findMatchingSlugs(allTags, link);
- if (tagSlugs) {
- let tagSlug = tagSlugs[0];
- return { pageType: 'tag', tagSlug, siteURL };
+ let tag = _.find(allTags, { link });
+ if (tag) {
+ return { pageType: 'tag', tagSlug: tag.slug, siteURL };
}
// see if it's pointing to a post
@@ -187,20 +185,6 @@ function findPostID(path) {
}
}
-function findMatchingSlugs(objects, link) {
- let matches = [];
- for (let object of objects) {
- let objectLink = _.trimEnd(object.link, '/') + '/';
- if (_.startsWith(link, objectLink)) {
- matches.push(object);
- }
- }
- if (matches.length > 0) {
- let slugs = _.map(_.sortBy(matches, 'link.length'), 'slug');
- return slugs;
- }
-}
-
let routes = {
'page': { path: '*' },
};
diff --git a/src/widgets/page-view.jsx b/src/widgets/page-view.jsx
index 729528f..630b81f 100644
--- a/src/widgets/page-view.jsx
+++ b/src/widgets/page-view.jsx
@@ -33,7 +33,7 @@ if (process.env.NODE_ENV !== 'production') {
const PropTypes = require('prop-types');
PageView.propTypes = {
- category: PropTypes.object,
+ page: PropTypes.object,
transform: PropTypes.func,
};
}
diff --git a/src/widgets/post-list.jsx b/src/widgets/post-list.jsx
index a5cddfa..49041b0 100644
--- a/src/widgets/post-list.jsx
+++ b/src/widgets/post-list.jsx
@@ -60,6 +60,7 @@ if (process.env.NODE_ENV !== 'production') {
const PropTypes = require('prop-types');
PostList.propTypes = {
+ posts: PropTypes.arrayOf(PropTypes.object),
route: PropTypes.instanceOf(Route),
minimum: PropTypes.number,
maximum: PropTypes.number,
diff --git a/src/widgets/side-nav.jsx b/src/widgets/side-nav.jsx
index 6eab3d8..336aa8e 100644
--- a/src/widgets/side-nav.jsx
+++ b/src/widgets/side-nav.jsx
@@ -155,12 +155,12 @@ class SideNavSync extends PureComponent {
renderCategory(category, i) {
let { route } = this.props;
- let { categorySlugs } = route.params;
+ let { categorySlug } = route.params;
let name = _.get(category, 'name', '');
let description = _.get(category, 'description', '');
let url = route.getObjectURL(category);
let className;
- if (category.slug === _.last(categorySlugs)) {
+ if (category.slug === categorySlug) {
className = 'selected';
}
return (
diff --git a/src/widgets/top-nav.jsx b/src/widgets/top-nav.jsx
index 8ff923e..7fc72c2 100644
--- a/src/widgets/top-nav.jsx
+++ b/src/widgets/top-nav.jsx
@@ -15,7 +15,7 @@ class TopNav extends AsyncComponent {
meanwhile.show(
);
props.system = await wp.fetchOne('/');
meanwhile.show(
);
- props.pages = await wp.fetchList('/wp/v2/pages/?parent=0', { minimum: '100%' });
+ props.pages = await wp.fetchList('/wp/v2/pages/', { minimum: '100%' });
return
;
}
}
@@ -61,6 +61,7 @@ class TopNavSync extends PureComponent {
renderPageLinkBar() {
let { pages } = this.props;
+ pages = _.filter(pages, { parent: 0 });
pages = _.sortBy(pages, 'menu_order');
return (