From 9a065a115b5e71cfb83f4287f38d1a03dbba0eda Mon Sep 17 00:00:00 2001
From: Florian Pichler
Date: Wed, 21 Aug 2019 12:17:14 +0200
Subject: [PATCH] Blog index pagination (#690)
* Allow ArrowLink to be reversed for pagination
* make blog components and template more generic
* split blog posts into pages
* make pagination links work
* make bundle split patterns more flexbile
* bundle blog posts with respective pages
* make blog page of each post its parent bundle
* drop blog.js bundle
* cache blog bundles in service worker
* format
---
config/routes-map.js | 55 ++++++++++------
ember-cli-build.js | 39 ++++++++----
lib/generate-blog/lib/collect-posts.js | 6 +-
lib/generate-blog/lib/components-builder.js | 37 ++++++++---
.../{start-page => list-page}/stylesheet.hbs | 5 +-
.../{start-page => list-page}/template.hbs | 63 +++++++++++--------
lib/service-workers/lib/worker-builder.js | 2 +-
lib/service-workers/workers/service-worker.js | 1 -
src/ui/components/ArrowLink/stylesheet.css | 24 +++++++
src/ui/components/ArrowLink/template.hbs | 2 +-
src/ui/styles/blocks/pagination.block.css | 6 ++
11 files changed, 169 insertions(+), 71 deletions(-)
rename lib/generate-blog/lib/templates/{start-page => list-page}/stylesheet.hbs (80%)
rename lib/generate-blog/lib/templates/{start-page => list-page}/template.hbs (64%)
create mode 100644 src/ui/styles/blocks/pagination.block.css
diff --git a/config/routes-map.js b/config/routes-map.js
index 33eed8b35d..f7508ad55d 100644
--- a/config/routes-map.js
+++ b/config/routes-map.js
@@ -1,24 +1,13 @@
'use strict';
const path = require('path');
+
+const _ = require('lodash');
+
const collectPosts = require('../lib/generate-blog/lib/collect-posts');
module.exports = function() {
let { posts, authors } = collectPosts(path.join(__dirname, '..', '_posts'));
- let blogPostRoutes = posts.reduce((acc, post) => {
- acc[`/blog/${post.queryPath}`] = {
- component: post.componentName,
- bundle: {
- asset: `/blog/${post.queryPath}.js`,
- module: `__blog-${post.queryPath}__`,
- },
- parentBundle: {
- asset: '/blog.js',
- },
- };
- return acc;
- }, {});
-
let blogAuthorsRoutes = authors.reduce((acc, author) => {
acc[`/blog/author/${author.twitter}`] = {
component: author.componentName,
@@ -26,19 +15,49 @@ module.exports = function() {
asset: `/blog/author-${author.twitter}.js`,
module: `__blog-author-${author.twitter}__`,
},
- parentBundle: {
- asset: '/blog.js',
- },
};
return acc;
}, {});
+ let blogPages = _.chunk(posts, 10);
+ let blogPostRoutes = {};
+ let blogPagesRoutes = {};
+ blogPages.forEach((pagePosts, i) => {
+ let page = i + 1;
+ let route;
+ if (page === 1) {
+ route = '/blog';
+ } else {
+ route = `/blog/page/${page}`;
+ }
+ blogPagesRoutes[route] = {
+ component: `PageBlogPage${page}`,
+ bundle: {
+ asset: `/blog/page/${page}.js`,
+ module: `__blog-page-${page}__`,
+ },
+ };
+
+ for (let post of pagePosts) {
+ blogPostRoutes[`/blog/${post.queryPath}`] = {
+ component: post.componentName,
+ bundle: {
+ asset: `/blog/${post.queryPath}.js`,
+ module: `__blog-${post.queryPath}__`,
+ },
+ parentBundle: {
+ asset: `/blog/page/${page}.js`,
+ },
+ };
+ }
+ });
+
let routes = {
+ ...blogPagesRoutes,
...blogPostRoutes,
...blogAuthorsRoutes,
'/': { component: 'PageHomepage' },
'/404': { component: 'Page404' },
- '/blog': { component: 'PageBlog', bundle: { asset: '/blog.js', module: '__blog__' } },
'/calendar': {
component: 'PageCalendar',
bundle: { asset: '/calendar.js', module: '__calendar__' },
diff --git a/ember-cli-build.js b/ember-cli-build.js
index 80ba63ff3f..20ff435bd9 100644
--- a/ember-cli-build.js
+++ b/ember-cli-build.js
@@ -69,10 +69,8 @@ class SimplabsApp extends GlimmerApp {
}
package(jsTree) {
- let [blogTree, mainSiteTree] = this._splitBundle(jsTree, {
+ let mainSiteTree = this._dropBundle(jsTree, {
componentPrefix: 'PageBlog',
- file: 'blog.js',
- moduleName: '__blog__',
});
let { posts, authors } = collectPosts(path.join(__dirname, '_posts'));
@@ -84,6 +82,7 @@ class SimplabsApp extends GlimmerApp {
});
return blogPostTree;
});
+
let blogAuthorTrees = authors.map(author => {
let [blogAuthorTree] = this._splitBundle(jsTree, {
componentPrefix: author.componentName,
@@ -93,6 +92,18 @@ class SimplabsApp extends GlimmerApp {
return blogAuthorTree;
});
+ let blogPages = _.chunk(posts, 10);
+ let blogPageTrees = blogPages.map((posts, i) => {
+ let page = i + 1;
+ let postPrefixes = posts.map(post => post.componentName).join('|');
+ let [blogPageTree] = this._splitBundle(jsTree, {
+ componentPrefix: `PageBlogPage${page}|${postPrefixes}`,
+ file: `blog/page/${page}.js`,
+ moduleName: `__blog-page-${page}__`,
+ });
+ return blogPageTree;
+ });
+
let [calendarTree, mainSiteNonCalendarTree] = this._splitBundle(mainSiteTree, {
componentPrefix: 'PageCalendar',
file: 'calendar.js',
@@ -136,9 +147,9 @@ class SimplabsApp extends GlimmerApp {
playbookTree,
talksTree,
recentContentTree,
- blogTree,
...blogPostTrees,
...blogAuthorTrees,
+ ...blogPageTrees,
]);
if (process.env.PRERENDER) {
@@ -177,14 +188,7 @@ class SimplabsApp extends GlimmerApp {
}
_splitBundle(appTree, bundle) {
- let mainBundleTree = new Funnel(appTree, {
- exclude: [`src/ui/components/${bundle.componentPrefix}*`],
- });
- let mainBundleJsTree = new Funnel(mainBundleTree, {
- include: ['**/*.js'],
- });
- let mainBundleModuleMap = this.buildResolutionMap(mainBundleJsTree);
- mainBundleTree = new MergeTrees([mainBundleTree, mainBundleModuleMap], { overwrite: true });
+ let mainBundleTree = this._dropBundle(appTree, bundle);
let bundleTree = new Funnel(appTree, {
exclude: [`src/ui/components/!(${bundle.componentPrefix})*`],
@@ -199,6 +203,17 @@ class SimplabsApp extends GlimmerApp {
return [bundleTree, mainBundleTree];
}
+ _dropBundle(appTree, bundle) {
+ let mainBundleTree = new Funnel(appTree, {
+ exclude: [`src/ui/components/+(${bundle.componentPrefix})*`],
+ });
+ let mainBundleJsTree = new Funnel(mainBundleTree, {
+ include: ['**/*.js'],
+ });
+ let mainBundleModuleMap = this.buildResolutionMap(mainBundleJsTree);
+ return new MergeTrees([mainBundleTree, mainBundleModuleMap], { overwrite: true });
+ }
+
_packageSplitBundle(bundleTree, bundle) {
return new Rollup(bundleTree, {
rollup: {
diff --git a/lib/generate-blog/lib/collect-posts.js b/lib/generate-blog/lib/collect-posts.js
index 8b663f17f3..8b1d9c3558 100644
--- a/lib/generate-blog/lib/collect-posts.js
+++ b/lib/generate-blog/lib/collect-posts.js
@@ -27,10 +27,12 @@ module.exports = function(folder) {
content,
};
});
-
- let authors = _.chain(posts)
+ posts = _.chain(posts)
.sortBy('meta.date')
.reverse()
+ .value();
+
+ let authors = _.chain(posts)
.uniqBy('meta.twitter')
.map(post => {
let componentName = buildAuthorComponentName(post);
diff --git a/lib/generate-blog/lib/components-builder.js b/lib/generate-blog/lib/components-builder.js
index bc1012c3c4..8e2f3c75ce 100644
--- a/lib/generate-blog/lib/components-builder.js
+++ b/lib/generate-blog/lib/components-builder.js
@@ -41,7 +41,8 @@ module.exports = class ComponentsBuilder extends BaseComponentsBuilder {
}
writeComponentFiles() {
- this._writeStartPageComponent();
+ let chunks = _.chunk(this.posts, 10);
+ chunks.forEach((chunk, i) => this._writeListPageComponent(chunk, i + 1, chunks.length));
for (let post of this.posts) {
let related = this._findRelatedPost(post);
@@ -58,17 +59,35 @@ module.exports = class ComponentsBuilder extends BaseComponentsBuilder {
}
}
- _writeStartPageComponent() {
- let [latest, ...rest] = this.posts;
- let data = {
- latest: this._preparePostTemplateData(latest),
- rest: rest.map(post => this._preparePostTemplateData(post)),
+ _writeListPageComponent(chunk, page, totalPages) {
+ let componentName = `PageBlogPage${page}`;
+
+ let data = {};
+ let posts = chunk;
+ if (page === 1) {
+ let [latest, ...rest] = chunk;
+ posts = rest;
+ data.latest = this._preparePostTemplateData(latest);
+ }
+ posts = posts.map(post => this._preparePostTemplateData(post));
+
+ let previousPage = page > 1 ? (page > 2 ? `/blog/page/${page - 1}` : '/blog') : null;
+ let nextPage = page < totalPages ? `/blog/page/${page + 1}` : null;
+
+ data = {
+ ...data,
+ posts,
+ previousPage,
+ nextPage,
};
- let componentTemplate = this.templates['start-page'].template(data);
+ let componentTemplate = this.templates['list-page'].template(data);
- let componentCssBlock = this.templates['start-page'].stylesheet();
+ data = {
+ componentName,
+ };
+ let componentCssBlock = this.templates['list-page'].stylesheet(data);
- this.writeComponent('PageBlog', componentTemplate, componentCssBlock);
+ this.writeComponent(componentName, componentTemplate, componentCssBlock);
}
_writePostComponent(post, related) {
diff --git a/lib/generate-blog/lib/templates/start-page/stylesheet.hbs b/lib/generate-blog/lib/templates/list-page/stylesheet.hbs
similarity index 80%
rename from lib/generate-blog/lib/templates/start-page/stylesheet.hbs
rename to lib/generate-blog/lib/templates/list-page/stylesheet.hbs
index 2888197d05..db717861a5 100644
--- a/lib/generate-blog/lib/templates/start-page/stylesheet.hbs
+++ b/lib/generate-blog/lib/templates/list-page/stylesheet.hbs
@@ -4,12 +4,13 @@
@block layout from "../../styles/blocks/layout.block.css";
@block offset from "../../styles/blocks/offset.block.css";
@block typography from "../../styles/blocks/typography.block.css";
+@block pagination from '../../styles/blocks/pagination.block.css';
:scope {
- block-name: PageBlog;
+ block-name: {{componentName}};
composes: 'layout';
}
.tweet {
-}
\ No newline at end of file
+}
diff --git a/lib/generate-blog/lib/templates/start-page/template.hbs b/lib/generate-blog/lib/templates/list-page/template.hbs
similarity index 64%
rename from lib/generate-blog/lib/templates/start-page/template.hbs
rename to lib/generate-blog/lib/templates/list-page/template.hbs
index 17f67df877..3e759ca1b3 100644
--- a/lib/generate-blog/lib/templates/start-page/template.hbs
+++ b/lib/generate-blog/lib/templates/list-page/template.hbs
@@ -36,33 +36,34 @@
>
-
-
-
- #
- {{latest.topic}}
-
-
-
- {{{latest.excerpt}}}
-
-
- {{latest.author.name}}
-
-
- {{latest.date}}
-
-
- Read Post
-
-
-
+
+ {{latest.date}}
+
+
+ Read Post
+
+
+
+ {{/if}}
- {{#each rest}}
+ {{#each posts}}
#
@@ -84,6 +85,18 @@
{{/each}}
+