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 ? ( +
+
{!isFirst && }
+
+ {Array.from({ length: numPages }, (_, i) => ( + + ))} +
+
{!isLast && }
+
+ ) : 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 (