diff --git a/README.md b/README.md
index b3a19230..1ea33f9c 100644
--- a/README.md
+++ b/README.md
@@ -16,16 +16,38 @@
## Table of Contents
+- [🎉 Features](#-features)
+ - [🎆 EXIF extraction](#-exif-extraction)
+ - [🕸 Related Posts](#-related-posts)
+ - [🏆 SEO component](#-seo-component)
+ - [📈 Matomo (formerly Piwik) analytics tracking](#-matomo-formerly-piwik-analytics-tracking)
+ - [gatsby-redirect-from](#-gatsby-redirect-from)
+ - [💎 Importing SVG assets](#-importing-svg-assets)
+ - [🍬 Typekit component](#-typekit-component)
+- [✨ Development](#-development)
+ - [🔮 Linting](#-linting)
+ - [🎈 Add a new project](#-add-a-new-project)
+- [🚚 Deployment](#-deployment)
+- [🏛 Licenses](#-licenses)
+ - [Posts](#-posts)
+ - [Photos & images](#-photos-images)
+
---
## 🎉 Features
The whole [blog](https://kremalicious.com) is a React-based Single Page App built with [Gatsby v2](https://www.gatsbyjs.org).
-### EXIF extraction
+### 🎆 EXIF extraction
...
+### 🕸 Related Posts
+
+Under each post a list of related posts is displayed which are based on the tags of the currently viewed post. Also allows loading more related posts in place.
+
+If you want to know how, have a look at the respective component under [`src/components/molecules/Pagination.jsx`](src/components/molecules/Pagination.jsx)
+
### 🏆 SEO component
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
diff --git a/content/posts/2015-08-02-simple-tor-setup-on-mac-os-x.md b/content/posts/2015-08-02-simple-tor-setup-on-mac-os-x.md
index f3f936b3..6f696049 100644
--- a/content/posts/2015-08-02-simple-tor-setup-on-mac-os-x.md
+++ b/content/posts/2015-08-02-simple-tor-setup-on-mac-os-x.md
@@ -10,10 +10,10 @@ updated: 2018-07-11 00:52:46+02:00
featured: true
tags:
- - goodies
- tutorial
- tor
- macos
+ - goodies
coinhive: true
---
diff --git a/gatsby-node.js b/gatsby-node.js
index 82f31a4f..ddf895c2 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -2,7 +2,6 @@ const path = require('path')
const fs = require('fs')
const yaml = require('js-yaml')
const { createFilePath } = require('gatsby-source-filesystem')
-const { paginate } = require('gatsby-awesome-pagination')
const fastExif = require('fast-exif')
const Fraction = require('fraction.js')
const dms2dec = require('dms2dec')
@@ -172,12 +171,13 @@ exports.createPages = ({ graphql, actions }) => {
}
const posts = result.data.allMarkdownRemark.edges
+ const numPages = Math.ceil(posts.length / itemsPerPage)
// Generate posts & posts index
- generateContent(createPage, posts)
+ generateContent(createPage, posts, numPages)
// Generate Tag Pages
- generateTagPages(createPage, posts)
+ generateTagPages(createPage, posts, numPages)
// create manual redirects
redirects.forEach(({ f, t }) => {
@@ -196,7 +196,7 @@ exports.createPages = ({ graphql, actions }) => {
const postsTemplate = path.resolve('src/templates/Posts.jsx')
-const generateContent = (createPage, posts) => {
+const generateContent = (createPage, posts, numPages) => {
const postTemplate = path.resolve('src/templates/Post.jsx')
// Create Post pages
@@ -210,69 +210,49 @@ const generateContent = (createPage, posts) => {
})
})
- // Create paginated front page
- paginate({
- createPage,
- items: posts,
- itemsPerPage: itemsPerPage,
- pathPrefix: '/',
- component: postsTemplate
+ // Create paginated Blog index pages
+ Array.from({ length: numPages }).forEach((_, i) => {
+ createPage({
+ path: i === 0 ? '/' : `/page/${i + 1}`,
+ component: postsTemplate,
+ context: {
+ limit: itemsPerPage,
+ skip: i * itemsPerPage,
+ numPages,
+ currentPageNumber: i + 1,
+ prevPage: i - 1,
+ nextPage: i + 2
+ }
+ })
})
}
const generateTagPages = (createPage, posts) => {
- const tagSet = new Set()
- const tagMap = new Map()
-
- posts.forEach(post => {
- if (post.node.frontmatter.tags) {
- post.node.frontmatter.tags.forEach(tag => {
- tagSet.add(tag)
-
- const array = tagMap.has(tag) ? tagMap.get(tag) : []
- array.push(post)
- tagMap.set(tag, array)
- })
- }
- })
-
- const tagList = Array.from(tagSet)
+ const tagList = arrayReducer(posts, 'tags')
tagList.forEach(tag => {
if (tag === 'goodies') return
// Create tag pages
createPage({
- path: `/tag/${tag}/`,
+ path: `/tags/${tag}/`,
component: postsTemplate,
context: { tag }
})
})
-
- // Object.keys(posts).forEach(tagName => {
- // const pageSize = 5
- // const pagesSum = Math.ceil(posts[tagName].length / pageSize)
-
- // for (let page = 1; page <= pagesSum; page++) {
- // createPage({
- // path:
- // page === 1
- // ? `/tag/${tagName.toLowerCase()}`
- // : `/tag/${tagName.toLowerCase()}/page/${page}`,
- // component: postsTemplate,
- // context: {
- // posts: paginate(posts[tagName], pageSize, page),
- // tag: tagName,
- // pagesSum,
- // page
- // }
- // })
- // }
- // })
}
-// function paginate(array, page_size, page_number) {
-// return array
-// .slice(0)
-// .slice((page_number - 1) * page_size, page_number * page_size)
-// }
+// https://www.adamjberkowitz.com/tags-and-categories-in-gatsby-js/
+const arrayReducer = (postsArray, type) => {
+ return (postsArray = postsArray
+ .map(({ node }) => {
+ return node.frontmatter[type]
+ })
+ .reduce((a, b) => {
+ return a.concat(b)
+ }, [])
+ .filter((type, index, array) => {
+ return array.indexOf(type) === index
+ })
+ .sort())
+}
diff --git a/package.json b/package.json
index ee4e17cd..7ed28edd 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,6 @@
"fast-exif": "^1.0.1",
"fraction.js": "^4.0.9",
"gatsby": "^2.0.12",
- "gatsby-awesome-pagination": "^0.3.1",
"gatsby-image": "^2.0.12",
"gatsby-plugin-catch-links": "^2.0.3",
"gatsby-plugin-favicon": "^3.1.4",
diff --git a/src/components/atoms/Image.jsx b/src/components/atoms/Image.jsx
index 3db63015..c9889c00 100644
--- a/src/components/atoms/Image.jsx
+++ b/src/components/atoms/Image.jsx
@@ -29,4 +29,12 @@ export const imageSizeDefault = graphql`
}
`
+export const imageSizeThumb = graphql`
+ fragment ImageFluidThumb on ImageSharp {
+ fluid(maxWidth: 200, maxHeight: 85, quality: 85, cropFocus: CENTER) {
+ ...GatsbyImageSharpFluid_withWebp_noBase64
+ }
+ }
+`
+
export default Image
diff --git a/src/components/atoms/PostTitle.module.scss b/src/components/atoms/PostTitle.module.scss
index e1a1edd3..5675f52e 100644
--- a/src/components/atoms/PostTitle.module.scss
+++ b/src/components/atoms/PostTitle.module.scss
@@ -27,15 +27,10 @@
@include ellipsis();
width: 100%;
- text-align: center;
color: $text-color;
font-family: $font-family-base;
font-size: $font-size-small;
padding: ($spacer/4) 0;
- max-width: 70%;
- margin: -($spacer) auto $spacer auto;
-
- @media (min-width: $screen-sm) {
- max-width: 50%;
- }
+ margin-top: -($spacer);
+ margin-bottom: $spacer;
}
diff --git a/src/components/molecules/Featured.jsx b/src/components/molecules/Featured.jsx
index 2d35de3c..8014677e 100644
--- a/src/components/molecules/Featured.jsx
+++ b/src/components/molecules/Featured.jsx
@@ -16,9 +16,7 @@ const query = graphql`
title
image {
childImageSharp {
- fluid(maxWidth: 300, maxHeight: 130, cropFocus: CENTER) {
- ...GatsbyImageSharpFluid_withWebp_noBase64
- }
+ ...ImageFluidThumb
}
}
}
diff --git a/src/components/molecules/Pagination.jsx b/src/components/molecules/Pagination.jsx
index d128ad77..9fdcc013 100644
--- a/src/components/molecules/Pagination.jsx
+++ b/src/components/molecules/Pagination.jsx
@@ -3,25 +3,61 @@ import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import styles from './Pagination.module.scss'
-const Pagination = ({ pageContext }) => {
- const { previousPagePath, nextPagePath } = pageContext
+const PageNumber = ({ i, current }) => (
+
+ {i + 1}
+
+)
+
+PageNumber.propTypes = {
+ i: PropTypes.number.isRequired,
+ current: PropTypes.bool
+}
+
+const PrevNext = ({ prevPagePath, nextPagePath }) => {
+ const link = prevPagePath || nextPagePath
+ const rel = prevPagePath ? 'prev' : 'next'
+ const title = prevPagePath ? 'Newer Posts' : 'Older Posts'
return (
-
- {nextPagePath ? (
-
- « Older Posts
-
- ) : null}
- {previousPagePath ? (
-
- Newer Posts »
-
- ) : null}
-
+
+ {prevPagePath ? '←' : '→'}
+
)
}
+PrevNext.propTypes = {
+ prevPagePath: PropTypes.string,
+ nextPagePath: PropTypes.string
+}
+
+const Pagination = ({ pageContext }) => {
+ const { currentPageNumber, numPages, prevPage, nextPage } = pageContext
+ const isFirst = currentPageNumber === 1
+ const isLast = currentPageNumber === numPages
+ const prevPagePath = currentPageNumber === 2 ? '/' : `/page/${prevPage}`
+ const nextPagePath = `/page/${nextPage}`
+
+ return nextPage > 1 ? (
+
+
+
+ {Array.from({ length: numPages }, (_, i) => (
+
+ ))}
+
+
+
+ ) : null
+}
+
Pagination.propTypes = {
pageContext: PropTypes.object.isRequired
}
diff --git a/src/components/molecules/Pagination.module.scss b/src/components/molecules/Pagination.module.scss
index 34b76656..63da475b 100644
--- a/src/components/molecules/Pagination.module.scss
+++ b/src/components/molecules/Pagination.module.scss
@@ -3,18 +3,41 @@
.pagination {
display: flex;
margin-top: $spacer * 2;
- margin-bottom: $spacer * 2;
-}
+ margin-bottom: $spacer;
+ justify-content: center;
-.paginationLink {
- flex: 1 1 50%;
- display: block;
+ > div {
+ &:first-child {
+ margin-right: $spacer;
+ }
- &:last-child {
- text-align: right;
- }
-
- &:only-child {
- text-align: left;
+ &:last-child {
+ margin-left: $spacer;
+ text-align: right;
+ }
}
}
+
+.number {
+ text-align: center;
+ width: 2rem;
+ height: 2rem;
+ line-height: 1.7;
+ display: inline-block;
+ border-radius: 50%;
+ border: 1px solid transparent;
+
+ &:hover,
+ &:focus {
+ background: rgba(255, 255, 255, .3);
+ border-color: darken($brand-grey-dimmed, 5%);
+ }
+}
+
+.current {
+ composes: number;
+ cursor: default;
+ pointer-events: none;
+ border: 1px solid darken($brand-grey-dimmed, 5%);
+ color: $brand-grey-light;
+}
diff --git a/src/components/molecules/PostMeta.jsx b/src/components/molecules/PostMeta.jsx
index e1a66d0d..0897e65f 100644
--- a/src/components/molecules/PostMeta.jsx
+++ b/src/components/molecules/PostMeta.jsx
@@ -3,17 +3,14 @@ import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import Time from 'react-time'
import slugify from 'slugify'
-import PostLinkActions from '../atoms/PostLinkActions'
import styles from './PostMeta.module.scss'
const PostMeta = ({ post, meta }) => {
- const { author, updated, tags, type, linkurl } = post.frontmatter
- const { date, slug } = post.fields
+ const { author, updated, tags, type } = post.frontmatter
+ const { date } = post.fields
return (