added read time on index and post pages

This commit is contained in:
Rick van Lieshout 2022-09-29 22:28:06 +02:00
parent 619758c175
commit 6a9ceb9670
22 changed files with 103 additions and 50 deletions

View File

@ -4,8 +4,6 @@ This is the repository for my personal blog/website [rickvanlieshout.com](https:
## Todo ## Todo
- "time to read" (https://www.gatsbyjs.com/plugins/gatsby-remark-reading-time/)
<!-- migrations --> <!-- migrations -->
- migrate resume - migrate resume
@ -14,4 +12,3 @@ This is the repository for my personal blog/website [rickvanlieshout.com](https:
<!-- optional stuff --> <!-- optional stuff -->
- release to sftp or gh-pages - release to sftp or gh-pages
- lighthouse doesn't like the blue... :)

View File

@ -52,7 +52,7 @@ const createPages: GatsbyNode["createPages"] = async ({ graphql, actions }) => {
createPage({ createPage({
path: node.fields.slug, path: node.fields.slug,
component: constants.templates.postTemplate, component: constants.templates.postTemplate,
context: { slug: node.fields.slug }, context: { slug: node.fields.slug, readingTime: node?.fields?.readingTime },
}); });
} }
}); });

View File

@ -1,15 +1,12 @@
import { GatsbyNode } from "gatsby"; import { GatsbyNode } from "gatsby";
import { createFilePath } from "gatsby-source-filesystem"; import { createFilePath } from "gatsby-source-filesystem";
import readingTime from "reading-time";
import * as constants from "./constants"; import * as constants from "./constants";
import * as types from "./types"; import * as types from "./types";
import * as utils from "./utils"; import * as utils from "./utils";
const onCreateNode: GatsbyNode["onCreateNode"] = ({ const onCreateNode: GatsbyNode["onCreateNode"] = ({ node, actions, getNode }) => {
node,
actions,
getNode,
}) => {
const { createNodeField } = actions; const { createNodeField } = actions;
if (node.internal.type === "MarkdownRemark") { if (node.internal.type === "MarkdownRemark") {
@ -31,12 +28,7 @@ const onCreateNode: GatsbyNode["onCreateNode"] = ({
if (tags) { if (tags) {
const value = tags.map((tag) => const value = tags.map((tag) =>
utils.concat( utils.concat(constants.routes.tagRoute, "/", utils.toKebabCase(tag), "/")
constants.routes.tagRoute,
"/",
utils.toKebabCase(tag),
"/",
),
); );
createNodeField({ node, name: "tagSlugs", value }); createNodeField({ node, name: "tagSlugs", value });
@ -47,11 +39,17 @@ const onCreateNode: GatsbyNode["onCreateNode"] = ({
constants.routes.categoryRoute, constants.routes.categoryRoute,
"/", "/",
utils.toKebabCase(category), utils.toKebabCase(category),
"/", "/"
); );
createNodeField({ node, name: "categorySlug", value }); createNodeField({ node, name: "categorySlug", value });
} }
createNodeField({
node,
name: "readingTime",
value: readingTime(node.rawMarkdownBody as string),
});
} }
}; };

View File

@ -19,6 +19,9 @@ const pagesQuery = async (graphql: CreatePagesArgs["graphql"]) => {
} }
fields { fields {
slug slug
readingTime {
text
}
} }
} }
} }

View File

@ -18,6 +18,9 @@ const postsQuery = async (graphql: CreatePagesArgs["graphql"]) => {
node { node {
fields { fields {
slug slug
readingTime {
text
}
} }
} }
} }

View File

@ -13,6 +13,9 @@ interface Fields {
slug?: string; slug?: string;
categorySlug?: string; categorySlug?: string;
tagSlugs?: Array<string>; tagSlugs?: Array<string>;
readingTime?: {
text: string;
};
} }
interface Node extends GatsbyNode { interface Node extends GatsbyNode {

13
package-lock.json generated
View File

@ -43,7 +43,8 @@
"react-cookie-consent": "^8.0.1", "react-cookie-consent": "^8.0.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-toggle": "^4.1.3" "react-toggle": "^4.1.3",
"reading-time": "^1.5.0"
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "^27.5.1", "@jest/globals": "^27.5.1",
@ -29075,6 +29076,11 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/reading-time": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz",
"integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg=="
},
"node_modules/recursive-readdir": { "node_modules/recursive-readdir": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@ -55669,6 +55675,11 @@
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
} }
}, },
"reading-time": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz",
"integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg=="
},
"recursive-readdir": { "recursive-readdir": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",

View File

@ -78,7 +78,8 @@
"react-cookie-consent": "^8.0.1", "react-cookie-consent": "^8.0.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-toggle": "^4.1.3" "react-toggle": "^4.1.3",
"reading-time": "^1.5.0"
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "^27.5.1", "@jest/globals": "^27.5.1",

View File

@ -5,7 +5,6 @@ import { Link } from "gatsby";
import * as styles from "./Feed.module.scss"; import * as styles from "./Feed.module.scss";
import { Edge } from "@/types"; import { Edge } from "@/types";
type Props = { type Props = {
edges: Array<Edge>; edges: Array<Edge>;
}; };
@ -17,12 +16,14 @@ const Feed: React.FC<Props> = ({ edges }: Props) => (
<div className={styles.meta}> <div className={styles.meta}>
<time <time
className={styles.time} className={styles.time}
dateTime={new Date(edge.node.frontmatter.date).toLocaleDateString( dateTime={new Date(edge.node.frontmatter.date).toLocaleDateString("en-US", {
"en-US", year: "numeric",
{ year: "numeric", month: "long", day: "numeric" }, month: "long",
)} day: "numeric",
})}
> >
{new Date(edge.node.frontmatter.date).toLocaleDateString("en-US", { {new Date(edge.node.frontmatter.date).toLocaleDateString("en-Us", {
day: "numeric",
year: "numeric", year: "numeric",
month: "long", month: "long",
})} })}
@ -39,11 +40,9 @@ const Feed: React.FC<Props> = ({ edges }: Props) => (
{edge.node.frontmatter.title} {edge.node.frontmatter.title}
</Link> </Link>
</h2> </h2>
<p className={styles.description}> <p className={styles.description}>{edge.node.frontmatter.description}</p>
{edge.node.frontmatter.description}
</p>
<Link className={styles.more} to={edge.node.fields.slug}> <Link className={styles.more} to={edge.node.fields.slug}>
Read Read ({edge.node.fields.readingTime?.text})
</Link> </Link>
</div> </div>
))} ))}

View File

@ -7,7 +7,7 @@ exports[`Feed renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -31,7 +31,8 @@ exports[`Feed renders correctly 1`] = `
<a <a
href="/posts/perfecting-the-art-of-perfection" href="/posts/perfecting-the-art-of-perfection"
> >
Read Read (
)
</a> </a>
</div> </div>
<div> <div>
@ -39,7 +40,7 @@ exports[`Feed renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -63,7 +64,8 @@ exports[`Feed renders correctly 1`] = `
<a <a
href="/posts/the-birth-of-movable-type" href="/posts/the-birth-of-movable-type"
> >
Read Read (
)
</a> </a>
</div> </div>
</div> </div>

View File

@ -9,13 +9,23 @@
.title { .title {
font-size: $typographic-base-font-size * 2; font-size: $typographic-base-font-size * 2;
font-weight: 600; font-weight: 600;
margin-bottom: 0px !important;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: $layout-post-width; max-width: $layout-post-width;
text-align: center; text-align: center;
@include line-height(1.65); @include line-height(1.65);
@include margin-top(1); @include margin-top(1);
@include margin-bottom(0); }
.subTitle {
font-size: $typographic-base-font-size;
font-style: italic;
margin-left: auto;
margin-right: auto;
margin-top: 0px;
max-width: $layout-post-width;
text-align: center;
} }
.body { .body {
@ -48,7 +58,7 @@
max-width: $layout-post-width; max-width: $layout-post-width;
} }
h2 > a { h2>a {
visibility: hidden; visibility: hidden;
} }
@ -80,7 +90,7 @@
@include margin-bottom(1.125); @include margin-bottom(1.125);
} }
h2 > a { h2>a {
visibility: unset; visibility: unset;
@include padding-right(1); @include padding-right(1);
} }

View File

@ -6,15 +6,17 @@ import * as styles from "./Content.module.scss";
interface Props { interface Props {
title: string; title: string;
body: string; body: string;
subTitle?: string;
} }
const Content: React.FC<Props> = ({ body, title }: Props) => { const Content: React.FC<Props> = ({ body, title, subTitle }: Props) => {
const { author } = useSiteMetadata(); const { author } = useSiteMetadata();
return ( return (
<> <>
<PostHeader author={author} /> <PostHeader author={author} />
<div className={styles.content}> <div className={styles.content}>
<h1 className={styles.title}>{title}</h1> <h1 className={styles.title}>{title}</h1>
<h2 className={styles.subTitle}>- {subTitle}</h2>
<div className={styles.body} dangerouslySetInnerHTML={{ __html: body }} /> <div className={styles.body} dangerouslySetInnerHTML={{ __html: body }} />
</div> </div>
</> </>

View File

@ -138,6 +138,9 @@ exports[`Content renders correctly 1`] = `
<h1> <h1>
Perfecting the Art of Perfection Perfecting the Art of Perfection
</h1> </h1>
<h2>
-
</h2>
<div <div
dangerouslySetInnerHTML={ dangerouslySetInnerHTML={
{ {

View File

@ -13,13 +13,13 @@ interface Props {
const Post: React.FC<Props> = ({ post }: Props) => { const Post: React.FC<Props> = ({ post }: Props) => {
const { html } = post; const { html } = post;
const { tagSlugs, slug } = post.fields; const { tagSlugs, slug, readingTime } = post.fields;
const { tags, title, date, disqusId } = post.frontmatter; const { tags, title, date, disqusId } = post.frontmatter;
return ( return (
<div className={styles.post}> <div className={styles.post}>
<div className={styles.content}> <div className={styles.content}>
<Content body={html} title={title} /> <Content body={html} title={title} subTitle={readingTime?.text} />
</div> </div>
<div className={styles.footer}> <div className={styles.footer}>

View File

@ -139,6 +139,9 @@ exports[`Post renders correctly 1`] = `
<h1> <h1>
Perfecting the Art of Perfection Perfecting the Art of Perfection
</h1> </h1>
<h2>
-
</h2>
<div <div
dangerouslySetInnerHTML={ dangerouslySetInnerHTML={
{ {

View File

@ -258,7 +258,7 @@ exports[`CategoryTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -282,7 +282,8 @@ exports[`CategoryTemplate renders correctly 1`] = `
<a <a
href="/posts/perfecting-the-art-of-perfection" href="/posts/perfecting-the-art-of-perfection"
> >
Read Read (
)
</a> </a>
</div> </div>
<div> <div>
@ -290,7 +291,7 @@ exports[`CategoryTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -314,7 +315,8 @@ exports[`CategoryTemplate renders correctly 1`] = `
<a <a
href="/posts/the-birth-of-movable-type" href="/posts/the-birth-of-movable-type"
> >
Read Read (
)
</a> </a>
</div> </div>
</div> </div>

View File

@ -54,6 +54,9 @@ export const query = graphql`
fields { fields {
categorySlug categorySlug
slug slug
readingTime {
text
}
} }
frontmatter { frontmatter {
description description

View File

@ -255,7 +255,7 @@ exports[`IndexTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -279,7 +279,8 @@ exports[`IndexTemplate renders correctly 1`] = `
<a <a
href="/posts/perfecting-the-art-of-perfection" href="/posts/perfecting-the-art-of-perfection"
> >
Read Read (
)
</a> </a>
</div> </div>
<div> <div>
@ -287,7 +288,7 @@ exports[`IndexTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -311,7 +312,8 @@ exports[`IndexTemplate renders correctly 1`] = `
<a <a
href="/posts/the-birth-of-movable-type" href="/posts/the-birth-of-movable-type"
> >
Read Read (
)
</a> </a>
</div> </div>
</div> </div>

View File

@ -38,6 +38,9 @@ export const query = graphql`
fields { fields {
slug slug
tagSlugs tagSlugs
readingTime {
text
}
} }
frontmatter { frontmatter {
date date

View File

@ -140,6 +140,9 @@ exports[`PostTemplate renders correctly 1`] = `
<h1> <h1>
Perfecting the Art of Perfection Perfecting the Art of Perfection
</h1> </h1>
<h2>
-
</h2>
<div <div
dangerouslySetInnerHTML={ dangerouslySetInnerHTML={
{ {

View File

@ -258,7 +258,7 @@ exports[`TagTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -282,7 +282,8 @@ exports[`TagTemplate renders correctly 1`] = `
<a <a
href="/posts/perfecting-the-art-of-perfection" href="/posts/perfecting-the-art-of-perfection"
> >
Read Read (
)
</a> </a>
</div> </div>
<div> <div>
@ -290,7 +291,7 @@ exports[`TagTemplate renders correctly 1`] = `
<time <time
dateTime="September 1, 2016" dateTime="September 1, 2016"
> >
September 2016 September 1, 2016
</time> </time>
<span /> <span />
<span> <span>
@ -314,7 +315,8 @@ exports[`TagTemplate renders correctly 1`] = `
<a <a
href="/posts/the-birth-of-movable-type" href="/posts/the-birth-of-movable-type"
> >
Read Read (
)
</a> </a>
</div> </div>
</div> </div>

View File

@ -2,6 +2,9 @@ interface Fields {
slug: string; slug: string;
categorySlug: string; categorySlug: string;
tagSlugs?: Array<string>; tagSlugs?: Array<string>;
readingTime?: {
text: string;
};
} }
export default Fields; export default Fields;