Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blog #49

Merged
merged 19 commits into from
Oct 31, 2022
Merged

Blog #49

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ module.exports = {
{
resolve: 'gatsby-remark-images',
options: {
maxWidth: 960,
maxWidth: 1280,
},
},
'gatsby-remark-autolink-headers', // must precede prismjs!
Expand Down
23 changes: 21 additions & 2 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ exports.onCreateNode = ({ node, getNode, actions }) => {
getNode,
basePath,
}).replace(directory, '');
const pathname = `/${baseURI}/${relativeFilePath}`.replace(multiSlashRE, '/');
const slug = node.frontmatter && node.frontmatter.slug;
const pathname = `/${baseURI}/${slug || relativeFilePath}`.replace(multiSlashRE, '/');

// Add a field to each markdown node to indicate its source instance. This
// is used by the gatsby-remark-prefix-relative-links plugin.
Expand Down Expand Up @@ -75,10 +76,28 @@ exports.createPages = async ({ graphql, actions }) => {
if (pathname === '/' && sourceInstanceName !== 'content') return;
const component = path.resolve(template);
createPage({
path: node.fields.pathname,
path: pathname,
component,
// Context is available in page queries as GraphQL variables.
context: { pathname, sourceInstanceName, template },
});
});
};

exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type MarkdownRemark implements Node {
frontmatter: Frontmatter
}
type Frontmatter {
author: String
canonical: String
# Date is the only required (non-nullable) field.
date: Date! @dateformat
slug: String
title: String
}
`
createTypes(typeDefs)
}
4 changes: 2 additions & 2 deletions lib/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,11 @@ const insertChildNode = (nav, child, indices) => {
};

const createSourceNav = (source) => {
const { title, config, baseURI } = source;
const { title, config, baseURI, page = null } = source;
let nav = {
title,
key: baseURI,
page: null,
page,
children: [],
};
const configYaml = loadConfigYaml(config);
Expand Down
25 changes: 11 additions & 14 deletions lib/sources.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const path = require('path');
const { assoc, compose, evolve, has, ifElse } = require('ramda');

const validateSource = (source = {}) => {
const { name, config, baseURI, remote, local } = source;
Expand All @@ -20,20 +21,16 @@ const validateSource = (source = {}) => {
return isValid;
};

const prepareSource = git => source => {
if (!source.remote) {
return {
...source,
basePath: source.local,
};
}
const { config, directory = '', name } = source;
return {
...source,
basePath: path.resolve(git, name, directory),
config: path.resolve(git, name, config),
};
};
const prepareSource = git => (s) => ifElse(
has('remote'),
compose(
evolve({
config: c => path.resolve(git, s.name, c),
}),
assoc('basePath', path.resolve(git, s.name, s.directory || '')),
),
assoc('basePath', s.local),
)(s);

const prepareSources = (sources, git) => {
return sources.filter(validateSource).map(prepareSource(git));
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
"gatsby-plugin-image": "^1.14.0",
"gatsby-plugin-manifest": "^3.14.0",
"gatsby-plugin-react-helmet": "^4.14.0",
"gatsby-plugin-sharp": "^3.14.0",
"gatsby-plugin-sharp": "^3.14.3",
"gatsby-plugin-sitemap": "^4.10.0",
"gatsby-remark-autolink-headers": "^4.11.0",
"gatsby-remark-images": "^4.0.0",
"gatsby-remark-images": "^4.2.0",
"gatsby-remark-prismjs": "^5.11.0",
"gatsby-source-filesystem": "^3.14.0",
"gatsby-source-git": "^1.1.0",
Expand Down
14 changes: 14 additions & 0 deletions site-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* designated `directory`. The default is `DOCS_TEMPLATE`.
*/
const DOCS_TEMPLATE = './src/templates/docs.js';
const BLOG_TEMPLATE = './src/templates/blog.js';

module.exports = {
siteMetadata: {
Expand Down Expand Up @@ -57,5 +58,18 @@ module.exports = {
branch: '1.x',
directory: 'docs/',
},
{
name: 'blog',
title: 'Blog',
baseURI: '/blog',
page: {
title: 'Blog',
pathname: '/blog',
},
remote: 'https://github.com/farmOS/farmOS-community-blog.git',
branch: 'main',
directory: 'posts/',
template: BLOG_TEMPLATE,
},
],
};
9 changes: 8 additions & 1 deletion src/components/seo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, article }) => {
const SEO = (props) => {
const {
article, canonical, description, image, title,
} = props;
const { pathname, origin } = useLocation()
const { site } = useStaticQuery(query)

Expand Down Expand Up @@ -56,6 +59,8 @@ const SEO = ({ title, description, image, article }) => {
)}

{seo.image && <meta name="twitter:image" content={seo.image} />}

{canonical && <link rel="canonical" href={canonical} />}
</Helmet>
)
}
Expand All @@ -67,13 +72,15 @@ SEO.propTypes = {
description: PropTypes.string,
image: PropTypes.string,
article: PropTypes.bool,
canonical: PropTypes.string,
}

SEO.defaultProps = {
title: null,
description: null,
image: null,
article: false,
canonical: null,
}

const query = graphql`
Expand Down
108 changes: 108 additions & 0 deletions src/pages/blog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as React from 'react';
import { Link } from 'gatsby-material-ui-components';
import { makeStyles } from '@material-ui/core/styles';
import { Box, Typography } from '@material-ui/core';
import Seo from '../components/seo';
import theme from '../theme';
import { graphql } from 'gatsby';

const DEFAULT_AUTHOR = 'the farmOS Community';
const DEFAULT_TITLE = 'farmOS Community Blog';

const useStyles = makeStyles({
main: {
'& h1': {
fontWeight: 300,
fontSize: '3rem',
lineHeight: 1.3,
letterSpacing: '-.01em',
margin: '0 0 1.25rem',
},
},
post: {
color: 'inherit',
textDecoration: 'none',
'& div': { paddingBottom: '2rem' },
'& h3': {
color: theme.palette.primary.light,
fontSize: '2rem',
lineHeight: 1.3,
},
'& h5': {
fontSize: '1rem',
lineHeight: 1.3,
'& span': {
fontWeight: 700,
},
},
'& p': {
color: theme.palette.text.hint,
},
'&:hover': {
textDecoration: 'none',
'& h3': { color: theme.palette.warning.main },
},
},
});

const BlogIndex = ({ data: { allMarkdownRemark } }) => {
const classes = useStyles();
const posts = allMarkdownRemark.edges.map(({ node }, i) => {
const {
excerpt, fields: { pathname }, frontmatter, headings,
} = node;
const { author, date, title } = frontmatter;
const h1 = headings.find(({ depth }) => depth === 1);
return (
<Link to={pathname} key={i} className={classes.post}>
<Box>
<Typography variant='h3'>
{title || h1?.value || DEFAULT_TITLE}
</Typography>
<Typography variant='h5'>
<span>{date}</span> by {author || DEFAULT_AUTHOR}
</Typography>
<Typography variant='body1'>{excerpt}</Typography>
</Box>
</Link>
);
});
return (
<>
<Seo title='farmOS | Blog'/>
<Box component='main' className={classes.main}>
<Typography variant='h1'>
Community Blog
</Typography>
{posts}
</Box>
</>
);
};

export const query = graphql`query BlogIndex {
allMarkdownRemark(
filter: { fields: { template: { eq: "./src/templates/blog.js" } } }
sort: {fields: frontmatter___date, order: DESC}
) {
edges {
node {
frontmatter {
author
date(formatString: "MMMM DD, YYYY")
title
}
excerpt
fields {
pathname
}
headings {
value
depth
}
}
}
}
}`;

export default BlogIndex
90 changes: 90 additions & 0 deletions src/templates/blog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { useEffect, useState } from 'react';
import { graphql } from 'gatsby';
import { makeStyles } from '@material-ui/core/styles';
import { Box, Typography } from '@material-ui/core';
import 'prismjs/themes/prism.css';
import theme from '../theme';
import Markdown from '../components/markdown';
import Seo from '../components/seo';

const DEFAULT_AUTHOR = 'the farmOS Community';
const DEFAULT_TITLE = 'farmOS Community Blog';

const useStyles = makeStyles({
heading: {
margin: '0 0 1.25rem',
},
headline: {
fontWeight: 300,
fontSize: '3rem',
lineHeight: 1.3,
},
byline: {
fontSize: '1rem',
lineHeight: 1.3,
color: theme.palette.text.secondary,
},
dateline: {
fontWeight: 700,
},
});

export default function BlogTemplate({ data }) {
const classes = useStyles();
const { markdownRemark: { frontmatter = {}, headings, html: initHtml } } = data;
const { canonical, date } = frontmatter;
let { author, title } = frontmatter;
if (!author) author = DEFAULT_AUTHOR;
const h1 = headings.find(({ depth }) => depth === 1);
if (!title && h1) title = h1.value;
if (!title && !h1) title = DEFAULT_TITLE;
const [html, setHtml] = useState(initHtml);
useEffect(() => {
if (h1) {
const div = document.createElement('div');
div.innerHTML = html;
const el = div.querySelector(`#${h1.id}`);
if (el) {
el.remove();
setHtml(div.outerHTML);
}
}
}, [h1, html]);

return (
<>
<Seo title={title} canonical={canonical}/>
<Box component='main'>
<Box className={classes.heading}>
<Typography variant='h1' className={classes.headline}>
{title}
</Typography>
<Typography className={classes.byline}>
<span className={classes.dateline}>{date}</span> by {author}
</Typography>
</Box>
<Markdown html={html}/>
</Box>
</>
);
};

export const query = graphql`
query($pathname: String!) {
markdownRemark(fields: { pathname: { eq: $pathname } }) {
frontmatter {
author
canonical
date(formatString: "MMMM DD, YYYY")
slug
title
}
html
headings {
id
value
depth
}
}
}
`;