Compare commits

..

No commits in common. "master" and "1.3" have entirely different histories.
master ... 1.3

24 changed files with 18275 additions and 16640 deletions

View File

@ -1,3 +0,0 @@
.cache/
node_modules/
public/

View File

@ -3,15 +3,19 @@ type: docker
name: Blog build and release
steps:
- name: build
image: node:alpine
commands:
- npm install
- npx gatsby build
- name: docker
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
username: stephan
password: eRFJ1R7UNo5zv1FFvLzv
repo: registry.while-false.de/blog
registry: registry.while-false.de
tags:
- 'latest'
- '1.4.0'
- '1.3.0'

View File

@ -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"]
}
}

View File

@ -1,10 +1 @@
FROM node:14.3 as build
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
FROM gatsbyjs/gatsby:onbuild

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -8,11 +8,11 @@ description: Initial commit - Introduction
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?
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

View File

@ -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:
* 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.
* 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
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).
# 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:
```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!

View File

@ -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.
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.
@ -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
* 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.
@ -74,7 +74,7 @@ steps:
commands:
- npm install
- npx gatsby build
- name: docker
image: plugins/docker
settings:
@ -91,7 +91,7 @@ For now I only require two steps:
1. install node.js dependencies and build the gatsby project
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:
[![Build Status](https://drone.while-false.de/api/badges/stephan/blog/status.svg)](https://drone.while-false.de/stephan/blog)

View File

@ -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).

View File

@ -1,6 +1,6 @@
// custom typefaces
import 'typeface-montserrat';
import 'typeface-merriweather';
import "typeface-montserrat"
import "typeface-merriweather"
import './src/styles/global.css';
import 'prismjs/themes/prism-solarizedlight.css';
import "./src/styles/global.css";
import "prismjs/themes/prism-solarizedlight.css";

View File

@ -1,129 +1,76 @@
module.exports = {
siteMetadata: {
title: 'While False Blog',
author: 'Stephan Dörfler',
description: 'Self-built developer blog based on gatsby.',
siteUrl: 'https://blog.while-false.de',
type: 'website',
title: `While False Blog`,
author: `Stephan Dörfler`,
description: `Self-built developer blog based on gatsby.`,
siteUrl: `https://blog.while-false.de`,
type: `website`,
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/blog`,
name: 'blog',
name: `blog`,
},
},
{
resolve: 'gatsby-source-filesystem',
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/assets`,
name: 'assets',
name: `assets`,
},
},
{
resolve: 'gatsby-transformer-remark',
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: 'gatsby-remark-images',
resolve: `gatsby-remark-images`,
options: {
maxWidth: 590,
},
},
{
resolve: 'gatsby-remark-responsive-iframe',
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: 'margin-bottom: 1.0725rem',
wrapperStyle: `margin-bottom: 1.0725rem`,
},
},
'gatsby-remark-prismjs',
'gatsby-remark-copy-linked-files',
'gatsby-remark-smartypants',
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
],
},
},
'gatsby-transformer-sharp',
'gatsby-plugin-sharp',
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: 'gatsby-plugin-matomo',
resolve: `gatsby-plugin-google-analytics`,
options: {
siteId: '1',
matomoUrl: 'https://matomo.while-false.de',
siteUrl: 'https://blog.while-false.de',
matomoPhpScript: 'matomo.php',
matomoJsScript: 'matomo.js',
//trackingId: `ADD YOUR TRACKING ID HERE`,
},
},
`gatsby-plugin-feed`,
{
resolve: 'gatsby-plugin-feed',
resolve: `gatsby-plugin-manifest`,
options: {
query: `
{
site {
siteMetadata {
title
description
siteUrl
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',
},
],
name: `While False Blog`,
short_name: `while-false`,
start_url: `/`,
background_color: `#f9ebe0`,
theme_color: `#3b7080`,
display: `minimal-ui`,
icon: `content/assets/gatsby-icon.png`,
},
},
`gatsby-plugin-offline`,
`gatsby-plugin-react-helmet`,
{
resolve: 'gatsby-plugin-manifest',
resolve: `gatsby-plugin-typography`,
options: {
name: 'While False Blog',
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',
pathToConfigModule: `src/utils/typography`,
},
},
],
};
}

View File

@ -1,10 +1,10 @@
const path = require('path');
const { createFilePath } = require('gatsby-source-filesystem');
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
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(
`
{
@ -25,18 +25,18 @@ exports.createPages = async ({ graphql, actions }) => {
}
}
`
);
)
if (result.errors) {
throw result.errors;
throw result.errors
}
// Create blog posts pages.
const posts = result.data.allMarkdownRemark.edges;
const posts = result.data.allMarkdownRemark.edges
posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node;
const next = index === 0 ? 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
createPage({
path: post.node.fields.slug,
@ -46,18 +46,19 @@ exports.createPages = async ({ graphql, actions }) => {
previous,
next,
},
});
});
};
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
if (node.internal.type === 'MarkdownRemark') {
const value = createFilePath({ node, getNode });
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: 'slug',
name: `slug`,
node,
value,
});
})
}
};
}

18040
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,47 +2,39 @@
"name": "while-false-blog",
"private": true,
"description": "A simple blog powered by Gatsby and Markdown",
"version": "1.0.0",
"author": "Stephan Dörfler <stephan@while-false.de>",
"version": "0.1.0",
"author": "Stephan Dörfler <st.doerfler@outlook.com>",
"dependencies": {
"gatsby": "^2.23.3",
"gatsby-cli": "^2.12.45",
"gatsby-image": "^2.4.6",
"gatsby-plugin-feed": "^2.5.4",
"gatsby-plugin-manifest": "^2.4.10",
"gatsby-plugin-matomo": "^0.8.3",
"gatsby-plugin-offline": "^3.2.8",
"gatsby-plugin-react-helmet": "^3.3.3",
"gatsby-plugin-sharp": "^2.6.10",
"gatsby-plugin-typography": "^2.5.3",
"gatsby-remark-copy-linked-files": "^2.3.4",
"gatsby-remark-images": "^3.3.9",
"gatsby-remark-prismjs": "^3.5.3",
"gatsby-remark-responsive-iframe": "^2.4.4",
"gatsby-remark-smartypants": "^2.3.3",
"gatsby-source-filesystem": "^2.3.10",
"gatsby-transformer-remark": "^2.8.14",
"gatsby-transformer-sharp": "^2.5.4",
"global": "^4.4.0",
"prismjs": "^1.20.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"gatsby": "^2.18.8",
"gatsby-image": "^2.2.34",
"gatsby-plugin-feed": "^2.3.23",
"gatsby-plugin-google-analytics": "^2.1.29",
"gatsby-plugin-manifest": "^2.2.31",
"gatsby-plugin-offline": "^3.0.27",
"gatsby-plugin-react-helmet": "^3.1.16",
"gatsby-plugin-sharp": "^2.3.5",
"gatsby-plugin-typography": "^2.3.18",
"gatsby-remark-copy-linked-files": "^2.1.31",
"gatsby-remark-images": "^3.1.35",
"gatsby-remark-prismjs": "^3.3.25",
"gatsby-remark-responsive-iframe": "^2.2.28",
"gatsby-remark-smartypants": "^2.1.17",
"gatsby-source-filesystem": "^2.1.40",
"gatsby-transformer-remark": "^2.6.39",
"gatsby-transformer-sharp": "^2.3.7",
"prismjs": "^1.17.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-helmet": "^5.2.1",
"react-typography": "^0.16.19",
"typeface-merriweather": "^0.0.72",
"typeface-montserrat": "^0.0.75",
"typeface-merriweather": "0.0.72",
"typeface-montserrat": "0.0.75",
"typography": "^0.16.19",
"typography-theme-moraga": "^0.16.19",
"typography-theme-wordpress-2016": "^0.16.19"
},
"devDependencies": {
"eslint": "^6.8.0",
"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"
"prettier": "^1.19.1"
},
"keywords": [
"gatsby"

View File

@ -5,11 +5,11 @@
* See: https://www.gatsbyjs.org/docs/use-static-query/
*/
import React from 'react';
import { useStaticQuery, graphql } from 'gatsby';
import Image from 'gatsby-image';
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Image from "gatsby-image"
import { rhythm } from '../utils/typography';
import { rhythm } from "../utils/typography"
const Bio = () => {
const data = useStaticQuery(graphql`
@ -27,13 +27,13 @@ const Bio = () => {
}
}
}
`);
`)
const { author } = data.site.siteMetadata;
const { author } = data.site.siteMetadata
return (
<div
style={{
display: 'flex',
display: `flex`,
marginBottom: rhythm(2.5),
}}
>
@ -44,22 +44,18 @@ const Bio = () => {
marginRight: rhythm(1 / 2),
marginBottom: 0,
minWidth: 50,
borderRadius: '100%',
borderRadius: `100%`,
}}
imgStyle={{
borderRadius: '50%',
borderRadius: `50%`,
}}
/>
<p>
Written by
{' '}
<strong>{author}</strong>
{' '}
who lives and works in Germany trying to build useful things.
{' '}
Written by <strong>{author}</strong> who lives and works in Germany trying to build useful things.
{` `}
</p>
</div>
);
};
)
}
export default Bio;
export default Bio

View File

@ -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;

View File

@ -1,13 +1,13 @@
import React from 'react';
import { Link } from 'gatsby';
import React from "react"
import { Link } from "gatsby"
import { rhythm, scale } from '../utils/typography';
import { rhythm, scale } from "../utils/typography"
class Layout extends React.Component {
render() {
const { location, title, children } = this.props;
const rootPath = `${__PATH_PREFIX__}/`;
let header;
const { location, title, children } = this.props
const rootPath = `${__PATH_PREFIX__}/`
let header
if (location.pathname === rootPath) {
header = (
@ -20,59 +20,56 @@ class Layout extends React.Component {
>
<Link
style={{
boxShadow: 'none',
textDecoration: 'none',
color: 'inherit',
boxShadow: `none`,
textDecoration: `none`,
color: `inherit`,
}}
to="/"
to={`/`}
>
{title}
</Link>
</h1>
);
)
} else {
header = (
<h3
style={{
fontFamily: 'Montserrat, sans-serif',
fontFamily: `Montserrat, sans-serif`,
marginTop: 0,
}}
>
<Link
style={{
boxShadow: 'none',
textDecoration: 'none',
color: 'inherit',
boxShadow: `none`,
textDecoration: `none`,
color: `inherit`,
}}
to="/"
to={`/`}
>
{title}
</Link>
</h3>
);
)
}
return (
<div
style={{
marginLeft: 'auto',
marginRight: 'auto',
marginLeft: `auto`,
marginRight: `auto`,
maxWidth: rhythm(24),
padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`,
padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`
}}
>
<header>{header}</header>
<main>{children}</main>
<footer>
©
{' '}
{new Date().getFullYear()}
, Built with
{' '}
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.org">Gatsby</a>
</footer>
</div>
);
)
}
}
export default Layout;
export default Layout

View File

@ -5,14 +5,12 @@
* See: https://www.gatsbyjs.org/docs/use-static-query/
*/
import React from 'react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { useStaticQuery, graphql } from 'gatsby';
import React from "react"
import PropTypes from "prop-types"
import Helmet from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"
function SEO({
description, lang, meta, title,
}) {
function SEO({ description, lang, meta, title }) {
const { site } = useStaticQuery(
graphql`
query {
@ -25,11 +23,11 @@ function SEO({
}
}
}
`,
);
`
)
const metaDescription = description || site.siteMetadata.description;
const metaType = 'website' || site.siteMetadata.type;
const metaDescription = description || site.siteMetadata.description
const metaType = `website` || site.siteMetadata.type
return (
<Helmet
@ -40,39 +38,39 @@ function SEO({
titleTemplate={`%s | ${site.siteMetadata.title}`}
meta={[
{
name: 'description',
name: `description`,
content: metaDescription,
},
{
property: 'og:title',
property: `og:title`,
content: title,
},
{
property: 'og:description',
property: `og:description`,
content: metaDescription,
},
{
property: 'og:type',
property: `og:type`,
content: metaType,
},
].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>
);
)
}
SEO.defaultProps = {
lang: 'en',
lang: `en`,
meta: [],
description: 'A selfmade developer blog.',
};
description: `A selfmade developer blog.`,
}
SEO.propTypes = {
description: PropTypes.string,
lang: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string.isRequired,
};
}
export default SEO;
export default SEO

View File

@ -1,13 +1,13 @@
import React from 'react';
import { graphql } from 'gatsby';
import React from "react"
import { graphql } from "gatsby"
import Layout from '../components/layout';
import SEO from '../components/seo';
import Layout from "../components/layout"
import SEO from "../components/seo"
class NotFoundPage extends React.Component {
render() {
const { data } = this.props;
const siteTitle = data.site.siteMetadata.title;
const { data } = this.props
const siteTitle = data.site.siteMetadata.title
return (
<Layout location={this.props.location} title={siteTitle}>
@ -15,11 +15,11 @@ class NotFoundPage extends React.Component {
<h1>Not Found</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
);
)
}
}
export default NotFoundPage;
export default NotFoundPage
export const pageQuery = graphql`
query {
@ -29,4 +29,4 @@ export const pageQuery = graphql`
}
}
}
`;
`

View File

@ -1,23 +1,23 @@
import React from 'react';
import { Link, graphql } from 'gatsby';
import React from "react"
import { Link, graphql } from "gatsby"
import Bio from '../components/bio';
import Layout from '../components/layout';
import SEO from '../components/seo';
import { rhythm } from '../utils/typography';
import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"
class BlogIndex extends React.Component {
render() {
const { data } = this.props;
const siteTitle = data.site.siteMetadata.title;
const posts = data.allMarkdownRemark.edges;
const { data } = this.props
const siteTitle = data.site.siteMetadata.title
const posts = data.allMarkdownRemark.edges
return (
<Layout location={this.props.location} title={siteTitle}>
<SEO title="All posts" />
<Bio />
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug;
const title = node.frontmatter.title || node.fields.slug
return (
<article key={node.fields.slug}>
<header>
@ -26,7 +26,7 @@ class BlogIndex extends React.Component {
marginBottom: rhythm(1 / 4),
}}
>
<Link style={{ boxShadow: 'none' }} to={node.fields.slug}>
<Link style={{ boxShadow: `none` }} to={node.fields.slug}>
{title}
</Link>
</h3>
@ -40,14 +40,29 @@ class BlogIndex extends React.Component {
/>
</section>
</article>
);
)
})}
</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`
query {
@ -72,4 +87,4 @@ export const pageQuery = graphql`
}
}
}
`;
`

View File

@ -1,7 +1,3 @@
body {
background-color: #e3dcc2;
}
#commento-textarea-root {
background-color: #00000010;
}
}

View File

@ -1,24 +1,23 @@
import React from 'react';
import { Link, graphql } from 'gatsby';
import React from "react"
import { Link, graphql } from "gatsby"
import Bio from '../components/bio';
import Layout from '../components/layout';
import SEO from '../components/seo';
import { rhythm, scale } from '../utils/typography';
import Commento from '../components/commento';
import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm, scale } from "../utils/typography"
class BlogPostTemplate extends React.Component {
render() {
const post = this.props.data.markdownRemark;
const siteTitle = this.props.data.site.siteMetadata.title;
const { previous, next } = this.props.pageContext;
const post = this.props.data.markdownRemark
const siteTitle = this.props.data.site.siteMetadata.title
const { previous, next } = this.props.pageContext
return (
<Layout location={this.props.location} title={siteTitle}>
<SEO
title={post.frontmatter.title}
description={post.frontmatter.description || post.excerpt}
type="article"
type='article'
/>
<article>
<header>
@ -33,7 +32,7 @@ class BlogPostTemplate extends React.Component {
<p
style={{
...scale(-1 / 5),
display: 'block',
display: `block`,
marginBottom: rhythm(1),
}}
>
@ -48,49 +47,41 @@ class BlogPostTemplate extends React.Component {
/>
<footer>
<Bio />
<div>
<h2>Comments</h2>
<Commento id={this.props.slug} />
</div>
</footer>
</article>
<nav>
<ul
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
listStyle: 'none',
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
}}
>
<li>
{previous && (
<Link to={previous.fields.slug} rel="prev">
{' '}
{previous.frontmatter.title}
{previous.frontmatter.title}
</Link>
)}
</li>
<li>
{next && (
<Link to={next.fields.slug} rel="next">
{next.frontmatter.title}
{' '}
{next.frontmatter.title}
</Link>
)}
</li>
</ul>
</nav>
</Layout>
);
)
}
}
export default BlogPostTemplate;
export default BlogPostTemplate
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!) {
@ -110,4 +101,4 @@ export const pageQuery = graphql`
}
}
}
`;
`

View File

@ -1,21 +1,23 @@
import Typography from 'typography';
import Moraga from 'typography-theme-moraga';
import Typography from "typography"
import Moraga from "typography-theme-moraga"
Moraga.overrideThemeStyles = () => ({
'a.gatsby-resp-image-link': {
boxShadow: 'none',
},
});
Moraga.overrideThemeStyles = () => {
return {
"a.gatsby-resp-image-link": {
boxShadow: `none`,
},
}
}
Moraga.headerFontFamily = ['Oxygen Mono'];
const typography = new Typography(Moraga);
// Hot reload typography in development.
if (process.env.NODE_ENV !== 'production') {
typography.injectStyles();
if (process.env.NODE_ENV !== `production`) {
typography.injectStyles()
}
export default typography;
export const { rhythm } = typography;
export const { scale } = typography;
export default typography
export const rhythm = typography.rhythm
export const scale = typography.scale

16169
yarn.lock

File diff suppressed because it is too large Load Diff