Compare commits
No commits in common. "master" and "1.3" have entirely different histories.
|
|
@ -1,3 +0,0 @@
|
||||||
.cache/
|
|
||||||
node_modules/
|
|
||||||
public/
|
|
||||||
14
.drone.yml
14
.drone.yml
|
|
@ -3,15 +3,19 @@ type: docker
|
||||||
name: Blog build and release
|
name: Blog build and release
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: node:alpine
|
||||||
|
commands:
|
||||||
|
- npm install
|
||||||
|
- npx gatsby build
|
||||||
|
|
||||||
- name: docker
|
- name: docker
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
username:
|
username: stephan
|
||||||
from_secret: docker_username
|
password: eRFJ1R7UNo5zv1FFvLzv
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: registry.while-false.de/blog
|
repo: registry.while-false.de/blog
|
||||||
registry: registry.while-false.de
|
registry: registry.while-false.de
|
||||||
tags:
|
tags:
|
||||||
- 'latest'
|
- 'latest'
|
||||||
- '1.4.0'
|
- '1.3.0'
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:react/recommended",
|
|
||||||
"airbnb"
|
|
||||||
],
|
|
||||||
"globals": {
|
|
||||||
"Atomics": "readonly",
|
|
||||||
"SharedArrayBuffer": "readonly"
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"jsx": true
|
|
||||||
},
|
|
||||||
"ecmaVersion": 11,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"comma-dangle": ["error", "never"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
Dockerfile
11
Dockerfile
|
|
@ -1,10 +1 @@
|
||||||
FROM node:14.3 as build
|
FROM gatsbyjs/gatsby:onbuild
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . ./
|
|
||||||
RUN yarn install -s --no-progress --prod
|
|
||||||
RUN yarn global add gatsby-cli
|
|
||||||
RUN npx gatsby build
|
|
||||||
|
|
||||||
FROM gatsbyjs/gatsby
|
|
||||||
COPY --from=build /app/public /pub
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
|
|
@ -8,11 +8,11 @@ description: Initial commit - Introduction
|
||||||
|
|
||||||
So I finally got around to make a blog. Now what?
|
So I finally got around to make a blog. Now what?
|
||||||
|
|
||||||
Honestly, I don't even know myself. For the last few years as a developer I consumed whatever the great, wide internet presented me with. I am a huge fan of just going out there and doing what I think is good. So now I try to do just that and begin to share what goes through my mind as a developer, DevOps-guy, team-lead and nerd. Maybe someone else can make use of some of it, maybe this is just a glorified public diary. I won't be posting regularly, maybe this project might also just die in a few days, weeks, month or any other point in time. Let's just see.
|
Honestly, I don't even know myself. For the last few years as a developer I consumed whatever the great, wide internet presented me with. I am a huge fan of just going out there and do what you think is good. So now I try to do just that and begin to share what goes through my mind as a developer, DevOps-guy, team-lead and nerd. Maybe someone else can make use of some of it, maybe this is just a glorified public diary. I won't be posting regularly, maybe this project might also just die in a few days, weeks, month or any other point in time. Let's just see.
|
||||||
|
|
||||||
# Who am I?
|
# Who am I?
|
||||||
|
|
||||||
I am Stephan, 28 (at the time of writing), and I try to build cool stuff with computers. Currently, I work in a company called [DEVDEER](https://devdeer.com/). We are a few enthusiasts (9, soon to be 10) helping businesses with consulting, developing, operating, migrating and integrating all around Microsoft's tools and technologies. When I'm not at work, I do stuff like this (tinkering with code), go out hiking, photograph or start cool DIY-projects I mostly never get around to finish.
|
I am Stephan, 28 (at the time of writing), and I try to build cool stuff with computers. Currently, I work in a company called [DEVDEER](https://devdeer.com/). We are a few enthusiasts (9, soon to be 10) helping businesses with consuting, developing, operating, migrating and integrating all around Microsoft's tools and technologies. When I'm not at work, I do stuff like this (tinkering with code), go out hiking, photograph or start cool DIY-projects I mostly never get around to finish.
|
||||||
|
|
||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ It took some time, but I figured as I only got around to deploy the blog just no
|
||||||
|
|
||||||
First, some thoughts for my requirements:
|
First, some thoughts for my requirements:
|
||||||
* I'm a developer. I experienced too much pain with wordpress, typo3 and similar CMSs in the past, so whatever I used for a blog had to be closer to what I feel confortable using.
|
* I'm a developer. I experienced too much pain with wordpress, typo3 and similar CMSs in the past, so whatever I used for a blog had to be closer to what I feel confortable using.
|
||||||
* I like to write [`markdown`](https://daringfireball.net/projects/markdown/). It's a nice, human readable syntax that can be easily converted to even nicer `HTML` content.
|
* I like to write [`markdown`](https://daringfireball.net/projects/markdown/). It's a nice, human readable syntax that can be easily converted even nicer `HTML` content.
|
||||||
* For WebApps, I like to use [`React`](https://reactjs.org/). It's a well maintained UI-framework with clean code structure and great extensibility.
|
* For WebApps, I like to use [`React`](https://reactjs.org/). It's a well maintained UI-framework with clean code structure and great extensibility.
|
||||||
* I am kind of cheap. I run a small virtual server with limited resources. To still have reasonable performance and a clean environment I run nothing but [`Docker`](https://www.docker.com/) on it.
|
* I am kind of cheap. I run a small virtual server with limited resources. To still have reasonable performance and a clean environment I run nothing but [`Docker`](https://www.docker.com/) on it.
|
||||||
|
|
||||||
|
|
@ -19,18 +19,16 @@ Some time ago I listened to [an episode of the podcast .Net rocks](https://dotne
|
||||||
# How this is built
|
# How this is built
|
||||||
|
|
||||||
Gatsby has a [template for blogs](https://github.com/gatsbyjs/gatsby-starter-blog). Using
|
Gatsby has a [template for blogs](https://github.com/gatsbyjs/gatsby-starter-blog). Using
|
||||||
```bash
|
|
||||||
npx gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog
|
npx gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog
|
||||||
```
|
|
||||||
|
|
||||||
I let gatsby create an instance of the blog template for me. From this template I got going with `npx gatsby develop` and started off with deleting a lot of files I didn't need. I also did some changes to the style. I have absolutely no background in anything even remotely related to making things look good, so I just went with what I had in my mind at that very moment (any feedback and suggestions are very welcome). The `gatsby new` command did also initialize a [`git`]() repository so I just had to commit my new changes. For better availability I then pushed the repository to [my self-hosted git server](https://code.while-false.de/stephan/blog).
|
I let gatsby create an instance of the blog template for me. From this template I got going with `npx gatsby develop` and started off with deleting a lot of files I didn't need. I also did some changes to the style. I have absolutely no background in anything even remotely related to making things look good, so I just went with what I had in my mind at that very moment (any feedback and suggestions are very welcome). The `gatsby new` command did also initialize a [`git`]() repository so I just had to commit my new changes. For better availability I then pushed the repository to [my self-hosted git server](https://code.while-false.de/stephan/blog).
|
||||||
|
|
||||||
# How this is run
|
# How this is run
|
||||||
|
|
||||||
I mentioned `docker` before. [It seems to be officially supported](https://github.com/gatsbyjs/gatsby-docker). I just went with the documentation and tried to get it to run. First, I created a `Dockerfile` in the project with just one line of content:
|
I mentioned `docker` before. [It seems to be officially supported](https://github.com/gatsbyjs/gatsby-docker). I just went with the documentation and tried to get it to run. First, I created a `Dockerfile` in the project with just one line of content:
|
||||||
```docker
|
|
||||||
FROM gatsbyjs/gatsby:onbuild
|
FROM gatsbyjs/gatsby:onbuild
|
||||||
```
|
|
||||||
|
|
||||||
Then, I could build first the gatsby project with `npx gatsby build` and use the optimized output from that to build a docker image with `docker build -t while-false/blog .` from the context of my project root. Next, I started a container from the newly created image with `docker run -d --name blog -p 8080:80 while-false/blog`. It worked on my laptop for `localhost:8080`, I saw the blog I just built. Nice!
|
Then, I could build first the gatsby project with `npx gatsby build` and use the optimized output from that to build a docker image with `docker build -t while-false/blog .` from the context of my project root. Next, I started a container from the newly created image with `docker run -d --name blog -p 8080:80 while-false/blog`. It worked on my laptop for `localhost:8080`, I saw the blog I just built. Nice!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ All steps on the server are handled from the terminal and typing the same comman
|
||||||
|
|
||||||
Whenever you do the same thing often, automation comes to mind. So the steps on the server are to be automated. I have some ideas for that.
|
Whenever you do the same thing often, automation comes to mind. So the steps on the server are to be automated. I have some ideas for that.
|
||||||
|
|
||||||
The relevant event I want to react on is the change in the blog. I consider a change as relevant, when a new commit happens on the `master` branch of the `git` repository of the blog. Luckily, `git` has a builtin concept for reacting on events on the server, called "server-side hooks". In my case the `git` server is an instance of `gitea`, so I looked up server side hooks in [the gitea documentation](https://docs.gitea.io/en-us/webhooks/). I quickly found the hook I needed:
|
The relevant event I want to react on is the change in the blog. I consider a change as relevant, when a new commit happens on the `master` branch of the `git` repository of the blog. Luckily, `git` has a builtin concept for reacting on events on the server, called "server-side hooks". In my case the `git` server is an instance of `gitea`, so I looked up server side hooks in [the gitea documentation](https://docs.gitea.io/en-us/webhooks/). I quickly found the hook I needed:
|
||||||
|
|
||||||
So I can make `git` notify some other component of each change. Now I need something to listen to these notifications and then execute the update-steps automatically.
|
So I can make `git` notify some other component of each change. Now I need something to listen to these notifications and then execute the update-steps automatically.
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ My first intention was to build that component myself. I know all the commands t
|
||||||
* Push the docker image to my private docker repository. Another authentication required
|
* Push the docker image to my private docker repository. Another authentication required
|
||||||
* On the host exchange the currently running docker container with the newly built one
|
* On the host exchange the currently running docker container with the newly built one
|
||||||
|
|
||||||
Around that time of planning I decided this isn't the way to go. At my job I rely heavily on [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) which conveniently covers all these tasks and requirements. But for my private free-time-projects I imposed the restriction on myself to run everything on my own server(s). But until now I only looked at the furthest cases on the automation spectrum: doing everything myself and have everything done by Microsoft in the cloud. I decided that the truth probably lies somewhere inbetween (as it does so often in life). I then looked at self-hosted CI/CD ("Continous Integration"/"Continous Delivery") systems.
|
Around that time of planning I decided this isn't the way to go. At my job I rely heavily on [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) which conveniently covers all these tasks and requirements. But for my private free-time-projects I imposed the restriction on myself to run everything on my own server(s). But until now I only looked at the furthest cases on the automation spectrum: doing everything myself and have everything done by Microsoft in the cloud. I decided that the truth probably lies somewhere inbetween (as so often in life). I then looked at self-hosted CI/CD ("Continous Integration"/"Continous Delivery") systems.
|
||||||
|
|
||||||
[Drone](https://drone.io) caught my eye, as it has full docker support, is open source and can have multiple, distributed workers. Perfect, I finally get to use the "sandbox" VPS I rent which just accumulates virtual dust. After reading the documentation, the setup was fairly easy.
|
[Drone](https://drone.io) caught my eye, as it has full docker support, is open source and can have multiple, distributed workers. Perfect, I finally get to use the "sandbox" VPS I rent which just accumulates virtual dust. After reading the documentation, the setup was fairly easy.
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- npm install
|
- npm install
|
||||||
- npx gatsby build
|
- npx gatsby build
|
||||||
|
|
||||||
- name: docker
|
- name: docker
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
|
|
@ -91,7 +91,7 @@ For now I only require two steps:
|
||||||
1. install node.js dependencies and build the gatsby project
|
1. install node.js dependencies and build the gatsby project
|
||||||
2. build the new docker image and push it to the registry
|
2. build the new docker image and push it to the registry
|
||||||
|
|
||||||
An additional benefit of the drone build is this beautiful badge, every project has nowadays, conveniently prepared as markdown:
|
An additional benefit of the drone build is this beatiful badge, every project has nowadays, conveniently prepared as markdown:
|
||||||
|
|
||||||
[](https://drone.while-false.de/stephan/blog)
|
[](https://drone.while-false.de/stephan/blog)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
---
|
|
||||||
title: Comments
|
|
||||||
date: "2020-09-05T17:56:22.339Z"
|
|
||||||
description: I would love to hear from you!
|
|
||||||
---
|
|
||||||
|
|
||||||
## Where do comments live?
|
|
||||||
|
|
||||||
I built this blog with [`GatsbyJS`](https://www.gatsbyjs.org/), which is optimized for static content. It works great for everything that exists when I build the code and create another docker image. But now I want to add comments from my readers. Which of course aren't static and don't exist on compile-time. So it seems GatsbyJS would not be the right tool for the job.
|
|
||||||
|
|
||||||
Luckily, GatsbyJS is expandable. There even is a [guide on how to integrate comments](https://www.gatsbyjs.com/docs/adding-comments/) in a blog on the gatsby website itself. But it assumes you just go and use some external service like disqus (although it does mention several alternatives). I didn't want to outsource that, the whole point of this blog is to run things myself and learn from that.
|
|
||||||
|
|
||||||
What I found was [Commento](https://commento.io/). It focusses around privacy and can be [self-hosted](https://docs.commento.io/installation/self-hosting/) instead of using an external service. It even has docker support. I quickly set up my very own instance on my server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d --name blog-commento \
|
|
||||||
-e "COMMENTO_ORIGIN=https://comments.while-false.de" \
|
|
||||||
-e "COMMENTO_POSTGRES=postgres://commentoDbUser:SuperSecretPassword@db_postgres/commento?sslmode=disable" \
|
|
||||||
registry.gitlab.com/commento/commento
|
|
||||||
```
|
|
||||||
*(In reality, I use a few more parameters and steps required for my specific hosting setup, which I will explain in a future post)*
|
|
||||||
|
|
||||||
So my blog itself can still be static and throught the magic of gatsby's automatic optimization blazing fast, while the dynamic comments are handled by an external server, which I can fully control as I host it myself.
|
|
||||||
|
|
||||||
## Include the comments in the blog
|
|
||||||
|
|
||||||
Next, I followed the [documentation for commento](https://docs.commento.io/installation/self-hosting/register-your-website/) and registered the blog on my commento instance. Easy.
|
|
||||||
|
|
||||||
Now to the tricky part. The static Gatsby.js website must embed the dynamic comments from the commento server. Again, I am lucky and found that [someone already did exactly that](https://itnext.io/adding-commento-to-react-apps-like-gatsby-871824fb57ae). With some small tweaks, this is what I use:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to add scripts to the page.
|
|
||||||
* @param {string} src The source path for the script to insert.
|
|
||||||
* @param {string} id The unique identifier for the script element to insert.
|
|
||||||
* @param {HTMLElement} parentElement The DOM element to insert the script into.
|
|
||||||
*/
|
|
||||||
const insertScript = (src, id, parentElement) => {
|
|
||||||
const script = window.document.createElement('script');
|
|
||||||
script.async = true;
|
|
||||||
script.src = src;
|
|
||||||
script.id = id;
|
|
||||||
parentElement.appendChild(script);
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to remove scripts from the page.
|
|
||||||
* @param {string} id The unique identifier for the script element to remove.
|
|
||||||
* @param {HTMLElement} parentElement The DOM element to remove the script from
|
|
||||||
*/
|
|
||||||
const removeScript = (id, parentElement) => {
|
|
||||||
const script = window.document.getElementById(id);
|
|
||||||
if (script) {
|
|
||||||
parentElement.removeChild(script);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const Commento = ({ id }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
// If there's no window there's nothing to do
|
|
||||||
if (!window) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { document } = window;
|
|
||||||
// In case the #commento container exists, the commento script can be added
|
|
||||||
if (document.getElementById('commento')) {
|
|
||||||
insertScript('https://comments.while-false.de/js/commento.js', 'commento-script', document.body);
|
|
||||||
}
|
|
||||||
// Cleanup; remove the script from the page
|
|
||||||
return () => removeScript('commento-script', document.body);
|
|
||||||
}, [id]);
|
|
||||||
return <div id="commento" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Commento;
|
|
||||||
```
|
|
||||||
|
|
||||||
This component itself is evaluated at runtime (it uses `useEffect`, which gatsby understands as non-static). It dynamically loads the scripts required by commento. The commento component is then included in my default blog-post template component by adding the line `<Commento id={this.props.slug} />`. The slug is the part of the URL after the hostname, i.e. `004-comments/` for this page. Thus, commento differentiates which comment belongs to which page.
|
|
||||||
|
|
||||||
Now, users can register and directly comment or comment anonymously with a required moderator review (which is me). Also, markdown, up- and downvoting, sorting, sticky, replies and moderation tools are included. Give it a try, I'd love to hear from you!
|
|
||||||
|
|
||||||
In the next few days I will tinker around with commento's settings for moderation notification emails, custom styling and comment analytics (number of views and number of comments).
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// custom typefaces
|
// custom typefaces
|
||||||
import 'typeface-montserrat';
|
import "typeface-montserrat"
|
||||||
import 'typeface-merriweather';
|
import "typeface-merriweather"
|
||||||
|
|
||||||
import './src/styles/global.css';
|
import "./src/styles/global.css";
|
||||||
import 'prismjs/themes/prism-solarizedlight.css';
|
import "prismjs/themes/prism-solarizedlight.css";
|
||||||
|
|
|
||||||
121
gatsby-config.js
121
gatsby-config.js
|
|
@ -1,129 +1,76 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
siteMetadata: {
|
siteMetadata: {
|
||||||
title: 'While False Blog',
|
title: `While False Blog`,
|
||||||
author: 'Stephan Dörfler',
|
author: `Stephan Dörfler`,
|
||||||
description: 'Self-built developer blog based on gatsby.',
|
description: `Self-built developer blog based on gatsby.`,
|
||||||
siteUrl: 'https://blog.while-false.de',
|
siteUrl: `https://blog.while-false.de`,
|
||||||
type: 'website',
|
type: `website`,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-source-filesystem',
|
resolve: `gatsby-source-filesystem`,
|
||||||
options: {
|
options: {
|
||||||
path: `${__dirname}/content/blog`,
|
path: `${__dirname}/content/blog`,
|
||||||
name: 'blog',
|
name: `blog`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-source-filesystem',
|
resolve: `gatsby-source-filesystem`,
|
||||||
options: {
|
options: {
|
||||||
path: `${__dirname}/content/assets`,
|
path: `${__dirname}/content/assets`,
|
||||||
name: 'assets',
|
name: `assets`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-transformer-remark',
|
resolve: `gatsby-transformer-remark`,
|
||||||
options: {
|
options: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-remark-images',
|
resolve: `gatsby-remark-images`,
|
||||||
options: {
|
options: {
|
||||||
maxWidth: 590,
|
maxWidth: 590,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-remark-responsive-iframe',
|
resolve: `gatsby-remark-responsive-iframe`,
|
||||||
options: {
|
options: {
|
||||||
wrapperStyle: 'margin-bottom: 1.0725rem',
|
wrapperStyle: `margin-bottom: 1.0725rem`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'gatsby-remark-prismjs',
|
`gatsby-remark-prismjs`,
|
||||||
'gatsby-remark-copy-linked-files',
|
`gatsby-remark-copy-linked-files`,
|
||||||
'gatsby-remark-smartypants',
|
`gatsby-remark-smartypants`,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'gatsby-transformer-sharp',
|
`gatsby-transformer-sharp`,
|
||||||
'gatsby-plugin-sharp',
|
`gatsby-plugin-sharp`,
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-matomo',
|
resolve: `gatsby-plugin-google-analytics`,
|
||||||
options: {
|
options: {
|
||||||
siteId: '1',
|
//trackingId: `ADD YOUR TRACKING ID HERE`,
|
||||||
matomoUrl: 'https://matomo.while-false.de',
|
|
||||||
siteUrl: 'https://blog.while-false.de',
|
|
||||||
matomoPhpScript: 'matomo.php',
|
|
||||||
matomoJsScript: 'matomo.js',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
`gatsby-plugin-feed`,
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-feed',
|
resolve: `gatsby-plugin-manifest`,
|
||||||
options: {
|
options: {
|
||||||
query: `
|
name: `While False Blog`,
|
||||||
{
|
short_name: `while-false`,
|
||||||
site {
|
start_url: `/`,
|
||||||
siteMetadata {
|
background_color: `#f9ebe0`,
|
||||||
title
|
theme_color: `#3b7080`,
|
||||||
description
|
display: `minimal-ui`,
|
||||||
siteUrl
|
icon: `content/assets/gatsby-icon.png`,
|
||||||
site_url: siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
feeds: [
|
|
||||||
{
|
|
||||||
serialize: ({ query: { site, allMarkdownRemark } }) => allMarkdownRemark.edges.map((edge) => ({
|
|
||||||
...edge.node.frontmatter,
|
|
||||||
description: edge.node.excerpt,
|
|
||||||
date: edge.node.frontmatter.date,
|
|
||||||
url: site.siteMetadata.siteUrl + edge.node.fields.slug,
|
|
||||||
guid: site.siteMetadata.siteUrl + edge.node.fields.slug,
|
|
||||||
custom_elements: [{ 'content:encoded': edge.node.html }],
|
|
||||||
})),
|
|
||||||
query: `
|
|
||||||
{
|
|
||||||
allMarkdownRemark(
|
|
||||||
sort: { order: DESC, fields: [frontmatter___date] },
|
|
||||||
) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
excerpt
|
|
||||||
html
|
|
||||||
fields { slug }
|
|
||||||
frontmatter {
|
|
||||||
title
|
|
||||||
date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
output: '/rss.xml',
|
|
||||||
title: 'while-false blog RSS Feed',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
`gatsby-plugin-offline`,
|
||||||
|
`gatsby-plugin-react-helmet`,
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-manifest',
|
resolve: `gatsby-plugin-typography`,
|
||||||
options: {
|
options: {
|
||||||
name: 'While False Blog',
|
pathToConfigModule: `src/utils/typography`,
|
||||||
short_name: 'while-false',
|
|
||||||
start_url: '/',
|
|
||||||
background_color: '#f9ebe0',
|
|
||||||
theme_color: '#3b7080',
|
|
||||||
display: 'minimal-ui',
|
|
||||||
icon: 'content/assets/logo.png',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'gatsby-plugin-offline',
|
|
||||||
'gatsby-plugin-react-helmet',
|
|
||||||
{
|
|
||||||
resolve: 'gatsby-plugin-typography',
|
|
||||||
options: {
|
|
||||||
pathToConfigModule: 'src/utils/typography',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
const path = require('path');
|
const path = require(`path`)
|
||||||
const { createFilePath } = require('gatsby-source-filesystem');
|
const { createFilePath } = require(`gatsby-source-filesystem`)
|
||||||
|
|
||||||
exports.createPages = async ({ graphql, actions }) => {
|
exports.createPages = async ({ graphql, actions }) => {
|
||||||
const { createPage } = actions;
|
const { createPage } = actions
|
||||||
|
|
||||||
const blogPost = path.resolve('./src/templates/blog-post.js');
|
const blogPost = path.resolve(`./src/templates/blog-post.js`)
|
||||||
const result = await graphql(
|
const result = await graphql(
|
||||||
`
|
`
|
||||||
{
|
{
|
||||||
|
|
@ -25,18 +25,18 @@ exports.createPages = async ({ graphql, actions }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
);
|
)
|
||||||
|
|
||||||
if (result.errors) {
|
if (result.errors) {
|
||||||
throw result.errors;
|
throw result.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create blog posts pages.
|
// Create blog posts pages.
|
||||||
const posts = result.data.allMarkdownRemark.edges;
|
const posts = result.data.allMarkdownRemark.edges
|
||||||
|
|
||||||
posts.forEach((post, index) => {
|
posts.forEach((post, index) => {
|
||||||
const previous = index === posts.length - 1 ? null : posts[index + 1].node;
|
const previous = index === posts.length - 1 ? null : posts[index + 1].node
|
||||||
const next = index === 0 ? null : posts[index - 1].node;
|
const next = index === 0 ? null : posts[index - 1].node
|
||||||
|
|
||||||
createPage({
|
createPage({
|
||||||
path: post.node.fields.slug,
|
path: post.node.fields.slug,
|
||||||
|
|
@ -46,18 +46,19 @@ exports.createPages = async ({ graphql, actions }) => {
|
||||||
previous,
|
previous,
|
||||||
next,
|
next,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.onCreateNode = ({ node, actions, getNode }) => {
|
exports.onCreateNode = ({ node, actions, getNode }) => {
|
||||||
const { createNodeField } = actions;
|
const { createNodeField } = actions
|
||||||
if (node.internal.type === 'MarkdownRemark') {
|
|
||||||
const value = createFilePath({ node, getNode });
|
if (node.internal.type === `MarkdownRemark`) {
|
||||||
|
const value = createFilePath({ node, getNode })
|
||||||
createNodeField({
|
createNodeField({
|
||||||
name: 'slug',
|
name: `slug`,
|
||||||
node,
|
node,
|
||||||
value,
|
value,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
|
|
@ -2,47 +2,39 @@
|
||||||
"name": "while-false-blog",
|
"name": "while-false-blog",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "A simple blog powered by Gatsby and Markdown",
|
"description": "A simple blog powered by Gatsby and Markdown",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"author": "Stephan Dörfler <stephan@while-false.de>",
|
"author": "Stephan Dörfler <st.doerfler@outlook.com>",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gatsby": "^2.23.3",
|
"gatsby": "^2.18.8",
|
||||||
"gatsby-cli": "^2.12.45",
|
"gatsby-image": "^2.2.34",
|
||||||
"gatsby-image": "^2.4.6",
|
"gatsby-plugin-feed": "^2.3.23",
|
||||||
"gatsby-plugin-feed": "^2.5.4",
|
"gatsby-plugin-google-analytics": "^2.1.29",
|
||||||
"gatsby-plugin-manifest": "^2.4.10",
|
"gatsby-plugin-manifest": "^2.2.31",
|
||||||
"gatsby-plugin-matomo": "^0.8.3",
|
"gatsby-plugin-offline": "^3.0.27",
|
||||||
"gatsby-plugin-offline": "^3.2.8",
|
"gatsby-plugin-react-helmet": "^3.1.16",
|
||||||
"gatsby-plugin-react-helmet": "^3.3.3",
|
"gatsby-plugin-sharp": "^2.3.5",
|
||||||
"gatsby-plugin-sharp": "^2.6.10",
|
"gatsby-plugin-typography": "^2.3.18",
|
||||||
"gatsby-plugin-typography": "^2.5.3",
|
"gatsby-remark-copy-linked-files": "^2.1.31",
|
||||||
"gatsby-remark-copy-linked-files": "^2.3.4",
|
"gatsby-remark-images": "^3.1.35",
|
||||||
"gatsby-remark-images": "^3.3.9",
|
"gatsby-remark-prismjs": "^3.3.25",
|
||||||
"gatsby-remark-prismjs": "^3.5.3",
|
"gatsby-remark-responsive-iframe": "^2.2.28",
|
||||||
"gatsby-remark-responsive-iframe": "^2.4.4",
|
"gatsby-remark-smartypants": "^2.1.17",
|
||||||
"gatsby-remark-smartypants": "^2.3.3",
|
"gatsby-source-filesystem": "^2.1.40",
|
||||||
"gatsby-source-filesystem": "^2.3.10",
|
"gatsby-transformer-remark": "^2.6.39",
|
||||||
"gatsby-transformer-remark": "^2.8.14",
|
"gatsby-transformer-sharp": "^2.3.7",
|
||||||
"gatsby-transformer-sharp": "^2.5.4",
|
"prismjs": "^1.17.1",
|
||||||
"global": "^4.4.0",
|
"react": "^16.12.0",
|
||||||
"prismjs": "^1.20.0",
|
"react-dom": "^16.12.0",
|
||||||
"react": "^16.13.1",
|
|
||||||
"react-dom": "^16.13.1",
|
|
||||||
"react-helmet": "^5.2.1",
|
"react-helmet": "^5.2.1",
|
||||||
"react-typography": "^0.16.19",
|
"react-typography": "^0.16.19",
|
||||||
"typeface-merriweather": "^0.0.72",
|
"typeface-merriweather": "0.0.72",
|
||||||
"typeface-montserrat": "^0.0.75",
|
"typeface-montserrat": "0.0.75",
|
||||||
"typography": "^0.16.19",
|
"typography": "^0.16.19",
|
||||||
"typography-theme-moraga": "^0.16.19",
|
"typography-theme-moraga": "^0.16.19",
|
||||||
"typography-theme-wordpress-2016": "^0.16.19"
|
"typography-theme-wordpress-2016": "^0.16.19"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.8.0",
|
"prettier": "^1.19.1"
|
||||||
"eslint-config-airbnb": "^18.1.0",
|
|
||||||
"eslint-plugin-import": "^2.21.2",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
|
||||||
"eslint-plugin-react": "^7.20.0",
|
|
||||||
"eslint-plugin-react-hooks": "^2.5.1",
|
|
||||||
"prettier": "^2.0.5"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"gatsby"
|
"gatsby"
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
* See: https://www.gatsbyjs.org/docs/use-static-query/
|
* See: https://www.gatsbyjs.org/docs/use-static-query/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import { useStaticQuery, graphql } from 'gatsby';
|
import { useStaticQuery, graphql } from "gatsby"
|
||||||
import Image from 'gatsby-image';
|
import Image from "gatsby-image"
|
||||||
|
|
||||||
import { rhythm } from '../utils/typography';
|
import { rhythm } from "../utils/typography"
|
||||||
|
|
||||||
const Bio = () => {
|
const Bio = () => {
|
||||||
const data = useStaticQuery(graphql`
|
const data = useStaticQuery(graphql`
|
||||||
|
|
@ -27,13 +27,13 @@ const Bio = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`);
|
`)
|
||||||
|
|
||||||
const { author } = data.site.siteMetadata;
|
const { author } = data.site.siteMetadata
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: `flex`,
|
||||||
marginBottom: rhythm(2.5),
|
marginBottom: rhythm(2.5),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -44,22 +44,18 @@ const Bio = () => {
|
||||||
marginRight: rhythm(1 / 2),
|
marginRight: rhythm(1 / 2),
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
minWidth: 50,
|
minWidth: 50,
|
||||||
borderRadius: '100%',
|
borderRadius: `100%`,
|
||||||
}}
|
}}
|
||||||
imgStyle={{
|
imgStyle={{
|
||||||
borderRadius: '50%',
|
borderRadius: `50%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Written by
|
Written by <strong>{author}</strong> who lives and works in Germany trying to build useful things.
|
||||||
{' '}
|
{` `}
|
||||||
<strong>{author}</strong>
|
|
||||||
{' '}
|
|
||||||
who lives and works in Germany trying to build useful things.
|
|
||||||
{' '}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Bio;
|
export default Bio
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to add scripts to the page.
|
|
||||||
* @param {string} src The source path for the script to insert.
|
|
||||||
* @param {string} id The unique identifier for the script element to insert.
|
|
||||||
* @param {HTMLElement} parentElement The DOM element to insert the script into.
|
|
||||||
*/
|
|
||||||
const insertScript = (src, id, parentElement) => {
|
|
||||||
const script = window.document.createElement('script');
|
|
||||||
script.async = true;
|
|
||||||
script.src = src;
|
|
||||||
script.id = id;
|
|
||||||
parentElement.appendChild(script);
|
|
||||||
return script;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to remove scripts from the page.
|
|
||||||
* @param {string} id The unique identifier for the script element to remove.
|
|
||||||
* @param {HTMLElement} parentElement The DOM element to remove the script from
|
|
||||||
*/
|
|
||||||
const removeScript = (id, parentElement) => {
|
|
||||||
const script = window.document.getElementById(id);
|
|
||||||
if (script) {
|
|
||||||
parentElement.removeChild(script);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const Commento = ({ id }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
// If there's no window there's nothing to do for us
|
|
||||||
if (!window) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { document } = window;
|
|
||||||
// In case our #commento container exists we can add our commento script
|
|
||||||
if (document.getElementById('commento')) {
|
|
||||||
insertScript('https://comments.while-false.de/js/commento.js', 'commento-script', document.body);
|
|
||||||
}
|
|
||||||
// Cleanup; remove the script from the page
|
|
||||||
return () => removeScript('commento-script', document.body);
|
|
||||||
}, [id]);
|
|
||||||
return <div id="commento" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Commento;
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import { Link } from 'gatsby';
|
import { Link } from "gatsby"
|
||||||
|
|
||||||
import { rhythm, scale } from '../utils/typography';
|
import { rhythm, scale } from "../utils/typography"
|
||||||
|
|
||||||
class Layout extends React.Component {
|
class Layout extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { location, title, children } = this.props;
|
const { location, title, children } = this.props
|
||||||
const rootPath = `${__PATH_PREFIX__}/`;
|
const rootPath = `${__PATH_PREFIX__}/`
|
||||||
let header;
|
let header
|
||||||
|
|
||||||
if (location.pathname === rootPath) {
|
if (location.pathname === rootPath) {
|
||||||
header = (
|
header = (
|
||||||
|
|
@ -20,59 +20,56 @@ class Layout extends React.Component {
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
style={{
|
style={{
|
||||||
boxShadow: 'none',
|
boxShadow: `none`,
|
||||||
textDecoration: 'none',
|
textDecoration: `none`,
|
||||||
color: 'inherit',
|
color: `inherit`,
|
||||||
}}
|
}}
|
||||||
to="/"
|
to={`/`}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
</h1>
|
</h1>
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
header = (
|
header = (
|
||||||
<h3
|
<h3
|
||||||
style={{
|
style={{
|
||||||
fontFamily: 'Montserrat, sans-serif',
|
fontFamily: `Montserrat, sans-serif`,
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
style={{
|
style={{
|
||||||
boxShadow: 'none',
|
boxShadow: `none`,
|
||||||
textDecoration: 'none',
|
textDecoration: `none`,
|
||||||
color: 'inherit',
|
color: `inherit`,
|
||||||
}}
|
}}
|
||||||
to="/"
|
to={`/`}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
</h3>
|
</h3>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginLeft: 'auto',
|
marginLeft: `auto`,
|
||||||
marginRight: 'auto',
|
marginRight: `auto`,
|
||||||
maxWidth: rhythm(24),
|
maxWidth: rhythm(24),
|
||||||
padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`,
|
padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<header>{header}</header>
|
<header>{header}</header>
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
<footer>
|
<footer>
|
||||||
©
|
© {new Date().getFullYear()}, Built with
|
||||||
{' '}
|
{` `}
|
||||||
{new Date().getFullYear()}
|
|
||||||
, Built with
|
|
||||||
{' '}
|
|
||||||
<a href="https://www.gatsbyjs.org">Gatsby</a>
|
<a href="https://www.gatsbyjs.org">Gatsby</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Layout;
|
export default Layout
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,12 @@
|
||||||
* See: https://www.gatsbyjs.org/docs/use-static-query/
|
* See: https://www.gatsbyjs.org/docs/use-static-query/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from "prop-types"
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from "react-helmet"
|
||||||
import { useStaticQuery, graphql } from 'gatsby';
|
import { useStaticQuery, graphql } from "gatsby"
|
||||||
|
|
||||||
function SEO({
|
function SEO({ description, lang, meta, title }) {
|
||||||
description, lang, meta, title,
|
|
||||||
}) {
|
|
||||||
const { site } = useStaticQuery(
|
const { site } = useStaticQuery(
|
||||||
graphql`
|
graphql`
|
||||||
query {
|
query {
|
||||||
|
|
@ -25,11 +23,11 @@ function SEO({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`
|
||||||
);
|
)
|
||||||
|
|
||||||
const metaDescription = description || site.siteMetadata.description;
|
const metaDescription = description || site.siteMetadata.description
|
||||||
const metaType = 'website' || site.siteMetadata.type;
|
const metaType = `website` || site.siteMetadata.type
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Helmet
|
<Helmet
|
||||||
|
|
@ -40,39 +38,39 @@ function SEO({
|
||||||
titleTemplate={`%s | ${site.siteMetadata.title}`}
|
titleTemplate={`%s | ${site.siteMetadata.title}`}
|
||||||
meta={[
|
meta={[
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: `description`,
|
||||||
content: metaDescription,
|
content: metaDescription,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
property: 'og:title',
|
property: `og:title`,
|
||||||
content: title,
|
content: title,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
property: 'og:description',
|
property: `og:description`,
|
||||||
content: metaDescription,
|
content: metaDescription,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
property: 'og:type',
|
property: `og:type`,
|
||||||
content: metaType,
|
content: metaType,
|
||||||
},
|
},
|
||||||
].concat(meta)}
|
].concat(meta)}
|
||||||
>
|
>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Oxygen+Mono&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css?family=Oxygen+Mono&display=swap" rel="stylesheet"></link>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SEO.defaultProps = {
|
SEO.defaultProps = {
|
||||||
lang: 'en',
|
lang: `en`,
|
||||||
meta: [],
|
meta: [],
|
||||||
description: 'A selfmade developer blog.',
|
description: `A selfmade developer blog.`,
|
||||||
};
|
}
|
||||||
|
|
||||||
SEO.propTypes = {
|
SEO.propTypes = {
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
lang: PropTypes.string,
|
lang: PropTypes.string,
|
||||||
meta: PropTypes.arrayOf(PropTypes.object),
|
meta: PropTypes.arrayOf(PropTypes.object),
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
export default SEO;
|
export default SEO
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from "gatsby"
|
||||||
|
|
||||||
import Layout from '../components/layout';
|
import Layout from "../components/layout"
|
||||||
import SEO from '../components/seo';
|
import SEO from "../components/seo"
|
||||||
|
|
||||||
class NotFoundPage extends React.Component {
|
class NotFoundPage extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { data } = this.props;
|
const { data } = this.props
|
||||||
const siteTitle = data.site.siteMetadata.title;
|
const siteTitle = data.site.siteMetadata.title
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout location={this.props.location} title={siteTitle}>
|
<Layout location={this.props.location} title={siteTitle}>
|
||||||
|
|
@ -15,11 +15,11 @@ class NotFoundPage extends React.Component {
|
||||||
<h1>Not Found</h1>
|
<h1>Not Found</h1>
|
||||||
<p>You just hit a route that doesn't exist... the sadness.</p>
|
<p>You just hit a route that doesn't exist... the sadness.</p>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NotFoundPage;
|
export default NotFoundPage
|
||||||
|
|
||||||
export const pageQuery = graphql`
|
export const pageQuery = graphql`
|
||||||
query {
|
query {
|
||||||
|
|
@ -29,4 +29,4 @@ export const pageQuery = graphql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import { Link, graphql } from 'gatsby';
|
import { Link, graphql } from "gatsby"
|
||||||
|
|
||||||
import Bio from '../components/bio';
|
import Bio from "../components/bio"
|
||||||
import Layout from '../components/layout';
|
import Layout from "../components/layout"
|
||||||
import SEO from '../components/seo';
|
import SEO from "../components/seo"
|
||||||
import { rhythm } from '../utils/typography';
|
import { rhythm } from "../utils/typography"
|
||||||
|
|
||||||
class BlogIndex extends React.Component {
|
class BlogIndex extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { data } = this.props;
|
const { data } = this.props
|
||||||
const siteTitle = data.site.siteMetadata.title;
|
const siteTitle = data.site.siteMetadata.title
|
||||||
const posts = data.allMarkdownRemark.edges;
|
const posts = data.allMarkdownRemark.edges
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout location={this.props.location} title={siteTitle}>
|
<Layout location={this.props.location} title={siteTitle}>
|
||||||
<SEO title="All posts" />
|
<SEO title="All posts" />
|
||||||
<Bio />
|
<Bio />
|
||||||
{posts.map(({ node }) => {
|
{posts.map(({ node }) => {
|
||||||
const title = node.frontmatter.title || node.fields.slug;
|
const title = node.frontmatter.title || node.fields.slug
|
||||||
return (
|
return (
|
||||||
<article key={node.fields.slug}>
|
<article key={node.fields.slug}>
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -26,7 +26,7 @@ class BlogIndex extends React.Component {
|
||||||
marginBottom: rhythm(1 / 4),
|
marginBottom: rhythm(1 / 4),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link style={{ boxShadow: 'none' }} to={node.fields.slug}>
|
<Link style={{ boxShadow: `none` }} to={node.fields.slug}>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
@ -40,14 +40,29 @@ class BlogIndex extends React.Component {
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BlogIndex;
|
export default BlogIndex
|
||||||
|
|
||||||
|
/** Matomo tracking */
|
||||||
|
var _paq = window._paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//matomo.while-false.de/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', '1']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/** End of Matomo tracking */
|
||||||
|
|
||||||
export const pageQuery = graphql`
|
export const pageQuery = graphql`
|
||||||
query {
|
query {
|
||||||
|
|
@ -72,4 +87,4 @@ export const pageQuery = graphql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
body {
|
body {
|
||||||
background-color: #e3dcc2;
|
background-color: #e3dcc2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#commento-textarea-root {
|
|
||||||
background-color: #00000010;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
import React from 'react';
|
import React from "react"
|
||||||
import { Link, graphql } from 'gatsby';
|
import { Link, graphql } from "gatsby"
|
||||||
|
|
||||||
import Bio from '../components/bio';
|
import Bio from "../components/bio"
|
||||||
import Layout from '../components/layout';
|
import Layout from "../components/layout"
|
||||||
import SEO from '../components/seo';
|
import SEO from "../components/seo"
|
||||||
import { rhythm, scale } from '../utils/typography';
|
import { rhythm, scale } from "../utils/typography"
|
||||||
import Commento from '../components/commento';
|
|
||||||
|
|
||||||
class BlogPostTemplate extends React.Component {
|
class BlogPostTemplate extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const post = this.props.data.markdownRemark;
|
const post = this.props.data.markdownRemark
|
||||||
const siteTitle = this.props.data.site.siteMetadata.title;
|
const siteTitle = this.props.data.site.siteMetadata.title
|
||||||
const { previous, next } = this.props.pageContext;
|
const { previous, next } = this.props.pageContext
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout location={this.props.location} title={siteTitle}>
|
<Layout location={this.props.location} title={siteTitle}>
|
||||||
<SEO
|
<SEO
|
||||||
title={post.frontmatter.title}
|
title={post.frontmatter.title}
|
||||||
description={post.frontmatter.description || post.excerpt}
|
description={post.frontmatter.description || post.excerpt}
|
||||||
type="article"
|
type='article'
|
||||||
/>
|
/>
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -33,7 +32,7 @@ class BlogPostTemplate extends React.Component {
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
...scale(-1 / 5),
|
...scale(-1 / 5),
|
||||||
display: 'block',
|
display: `block`,
|
||||||
marginBottom: rhythm(1),
|
marginBottom: rhythm(1),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -48,49 +47,41 @@ class BlogPostTemplate extends React.Component {
|
||||||
/>
|
/>
|
||||||
<footer>
|
<footer>
|
||||||
<Bio />
|
<Bio />
|
||||||
<div>
|
|
||||||
<h2>Comments</h2>
|
|
||||||
<Commento id={this.props.slug} />
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<ul
|
<ul
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: `flex`,
|
||||||
flexWrap: 'wrap',
|
flexWrap: `wrap`,
|
||||||
justifyContent: 'space-between',
|
justifyContent: `space-between`,
|
||||||
listStyle: 'none',
|
listStyle: `none`,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
{previous && (
|
{previous && (
|
||||||
<Link to={previous.fields.slug} rel="prev">
|
<Link to={previous.fields.slug} rel="prev">
|
||||||
←
|
← {previous.frontmatter.title}
|
||||||
{' '}
|
|
||||||
{previous.frontmatter.title}
|
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{next && (
|
{next && (
|
||||||
<Link to={next.fields.slug} rel="next">
|
<Link to={next.fields.slug} rel="next">
|
||||||
{next.frontmatter.title}
|
{next.frontmatter.title} →
|
||||||
{' '}
|
|
||||||
→
|
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BlogPostTemplate;
|
export default BlogPostTemplate
|
||||||
|
|
||||||
export const pageQuery = graphql`
|
export const pageQuery = graphql`
|
||||||
query BlogPostBySlug($slug: String!) {
|
query BlogPostBySlug($slug: String!) {
|
||||||
|
|
@ -110,4 +101,4 @@ export const pageQuery = graphql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,23 @@
|
||||||
import Typography from 'typography';
|
import Typography from "typography"
|
||||||
import Moraga from 'typography-theme-moraga';
|
import Moraga from "typography-theme-moraga"
|
||||||
|
|
||||||
Moraga.overrideThemeStyles = () => ({
|
Moraga.overrideThemeStyles = () => {
|
||||||
'a.gatsby-resp-image-link': {
|
return {
|
||||||
boxShadow: 'none',
|
"a.gatsby-resp-image-link": {
|
||||||
},
|
boxShadow: `none`,
|
||||||
});
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Moraga.headerFontFamily = ['Oxygen Mono'];
|
Moraga.headerFontFamily = ['Oxygen Mono'];
|
||||||
|
|
||||||
const typography = new Typography(Moraga);
|
const typography = new Typography(Moraga);
|
||||||
|
|
||||||
// Hot reload typography in development.
|
// Hot reload typography in development.
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== `production`) {
|
||||||
typography.injectStyles();
|
typography.injectStyles()
|
||||||
}
|
}
|
||||||
|
|
||||||
export default typography;
|
export default typography
|
||||||
export const { rhythm } = typography;
|
export const rhythm = typography.rhythm
|
||||||
export const { scale } = typography;
|
export const scale = typography.scale
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue