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

Redesign #55

Merged
merged 1 commit into from Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .eslintrc.json
Expand Up @@ -4,5 +4,11 @@
"pragma": "React",
"version": "16.7.0"
}
},
"parserOptions": {
"ecmaVersion": 2017
},
"env": {
"es6": true
}
}
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -70,7 +70,6 @@ typings/
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
Expand Down
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions README.md
@@ -1,3 +1,20 @@
# Trevor Harmon's Portfolio Site
# thetrevorharmon.com

This is my portfolio site. Feel free to look around!
Hi there! 👋 This is my blog & portfolio site. It's built on [Gatsby](https://gatsby.org) and written in Typescript, with the styling done in SASS modules. The data is living in Contentful, a headless CMS, and is pulled at build-time. The site is hosted on Netlify. 💪

## Getting Started

Getting started with this repo is fairly straightforward:

1. Go to Contentful, Mailchimp, and Google Analytics to find all the necessary pieces for your .env file.
2. Create two .env files: `.env.development` and `.env.production`. Stick the _preview_ Contentful token in the `.env.development` file, and stick the normal one in the prod file. You can refer to `.env.sample` to know what you need to find.
3. Run `yarn install`.
4. Run `yarn start`.

That's it!

## Hosting

[![Netlify Status](https://api.netlify.com/api/v1/badges/c6ab75d8-c5c5-4237-9ae8-c2320b3e7cac/deploy-status)](https://app.netlify.com/sites/thetrevorharmon/deploys)

This site has a continuous deployment set up with Netlify. Any time there is a change to master, Netlify will automatically redeploy the site (usually takes <7 minutes).
1 change: 0 additions & 1 deletion gatsby-browser.js
Expand Up @@ -4,7 +4,6 @@
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/

// You can delete this file if you're not using it
import React from 'react';
import {ThemeProvider} from './src/context/ThemeContext';
export const wrapRootElement = ({element}) => (
Expand Down
103 changes: 36 additions & 67 deletions gatsby-config.js
Expand Up @@ -7,10 +7,12 @@ require('dotenv').config({
path: `.env.${environment}`,
});

const Utils = require('./gatsby-utils');

module.exports = {
siteMetadata: {
title: `The Trevor Harmon`,
tagline: `I’ve been doing design & development work for about ten years. I love building beautiful, usable things.`,
tagline: `I write code for Shopify, and (sometimes) write things on my blog.`,
description: `This is the portfolio site for all of the design and development work of Trevor Harmon.`,
siteUrl: `https://thetrevorharmon.com`,
feedUrl: `https://thetrevorharmon.com/rss.xml`,
Expand Down Expand Up @@ -46,6 +48,8 @@ module.exports = {
resolve: `gatsby-plugin-favicon`,
options: {
logo: './static/favicon.png',
background: 'transparent',
version: '2.0',
icons: {
appleStartup: false,
},
Expand All @@ -66,16 +70,18 @@ module.exports = {
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
`gatsby-remark-embed-gist`, // for embedding gists
`gatsby-remark-prismjs`, // for code highlighting
{
resolve: `gatsby-remark-images-contentful`,
options: {
maxWidth: 800,
linkImagesToOriginal: false,
backgroundColor: `transparent`,
},
},
// {
// // This plugin was causing me grief previously 👇
// // https://github.com/gatsbyjs/gatsby/issues/11867
// resolve: `gatsby-remark-images-contentful`,
// options: {
// maxWidth: 800,
// linkImagesToOriginal: false,
// backgroundColor: `transparent`,
// withWebp: true,
// },
// },
],
},
},
Expand All @@ -97,40 +103,9 @@ module.exports = {
serialize: ({
query: {site, allContentfulBlogPost, allContentfulLinkPost},
}) => {
// This combines blog posts and link posts into a single array of posts
// First combines and then sorts according to date (newest first)
const combinePostTypes = (
blogPosts,
linkPosts,
order = 'desc',
) => {
const orderMultiplier = order === 'desc' ? 1 : -1;

const posts = [
// blog posts
...blogPosts.edges.map((edge) => edge.node),
// link posts
...linkPosts.edges.map((edge) => edge.node),
].sort((firstDate, secondDate) => {
const a = new Date(firstDate.date);
const b = new Date(secondDate.date);

if (a < b) {
return 1 * orderMultiplier;
}
if (a > b) {
return -1 * orderMultiplier;
}

return 0;
});

return posts;
};

const posts = combinePostTypes(
allContentfulBlogPost,
allContentfulLinkPost,
const posts = Utils.combinePostTypes(
allContentfulBlogPost.nodes,
allContentfulLinkPost.nodes,
);

return posts.map((post) => {
Expand All @@ -157,17 +132,15 @@ module.exports = {
limit: 1000,
sort: { order: DESC, fields: [date] },
) {
edges {
node {
title
slug
description
date
body {
childMarkdownRemark {
html
excerpt
}
nodes {
title
slug
description
date
body {
childMarkdownRemark {
html
excerpt
}
}
}
Expand All @@ -176,18 +149,14 @@ module.exports = {
limit: 1000,
sort: { order: DESC, fields: [date] },
) {
edges {
node {
title
slug
# add description back in when needed
# description
date
body {
childMarkdownRemark {
html
excerpt
}
nodes {
title
slug
date
body {
childMarkdownRemark {
html
excerpt
}
}
}
Expand Down
130 changes: 46 additions & 84 deletions gatsby-node.js
Expand Up @@ -4,118 +4,80 @@
* See: https://www.gatsbyjs.org/docs/node-apis/
*/
const path = require(`path`);

// This combines blog posts and link posts into a single array of posts
// First combines and then sorts according to date (newest first)
const combinePostTypes = (blogPosts, linkPosts, order = 'desc') => {
const orderMultiplier = order === 'desc' ? 1 : -1;

const posts = [
// blog posts
...blogPosts.edges.map((edge) => edge.node),
// link posts
...linkPosts.edges.map((edge) => edge.node),
].sort((firstDate, secondDate) => {
const a = new Date(firstDate.date);
const b = new Date(secondDate.date);

if (a < b) {
return 1 * orderMultiplier;
}
if (a > b) {
return -1 * orderMultiplier;
}

return 0;
});

return posts;
};

// Given a type, this returns the location of the template for the type
// It throws an error when it encounters a type that doesn't exist
const pathTemplateForPostType = (type) => {
const paths = {
ContentfulBlogPost: `./src/templates/post/blog/blogPostTemplate.tsx`,
ContentfulLinkPost: `./src/templates/post/blog/linkPostTemplate.tsx`,
ContentfulProject: `./src/templates/project/projectTemplate.tsx`,
};

if (!paths[type]) {
throw new Error(`Cannot find template path for type: ${type}`);
}

return paths[type];
};
const Utils = require('./gatsby-utils');

exports.createPages = ({graphql, actions}) => {
const {createPage} = actions;
return new Promise((resolve, reject) => {
graphql(`
{
allContentfulProject {
edges {
node {
title
slug
internal {
type
}
allContentfulProject(
sort: {order: DESC, fields: [projectCompletionDate]}
) {
nodes {
title
slug
projectCompletionDate(formatString: "DD MMM YYYY")
internal {
type
}
}
}
allContentfulBlogPost(sort: {order: DESC, fields: [date]}) {
edges {
node {
title
slug
internal {
type
}
nodes {
title
slug
date(formatString: "DD MMM YYYY")
internal {
type
}
}
}
allContentfulLinkPost(sort: {order: DESC, fields: [date]}) {
edges {
node {
title
slug
internal {
type
}
nodes {
title
slug
date(formatString: "DD MMM YYYY")
internal {
type
}
}
}
}
`).then((result) => {
result.data.allContentfulProject.edges.forEach(({node}) => {
result.data.allContentfulProject.nodes.forEach((project, _, projects) => {
const recommendedProjects = Utils.getRecommendedItems(
project,
projects,
'projectCompletionDate',
);

createPage({
path: `projects/${node.slug}`,
component: path.resolve(pathTemplateForPostType(node.internal.type)),
path: `projects/${project.slug}`,
component: path.resolve(
Utils.pathTemplateForPostType(project.internal.type),
),
context: {
slug: node.slug,
slug: project.slug,
recommendedProjects: recommendedProjects,
},
});
});

const posts = combinePostTypes(
result.data.allContentfulBlogPost,
result.data.allContentfulLinkPost,
);

posts.forEach((node, index) => {
// these give an easy way to figure out which post is considered
// the next newer/older post from within a blog post
const newerPost = index > 0 ? posts[index - 1] : null;
const olderPost = index < posts.length - 1 ? posts[index + 1] : null;
Utils.combinePostTypes(
result.data.allContentfulBlogPost.nodes,
result.data.allContentfulLinkPost.nodes,
).forEach((post, _, posts) => {
const recommendedPosts = Utils.getRecommendedItems(post, posts, 'date');

createPage({
path: `blog/${node.slug}`,
component: path.resolve(pathTemplateForPostType(node.internal.type)),
path: `blog/${post.slug}`,
component: path.resolve(
Utils.pathTemplateForPostType(post.internal.type),
),
context: {
slug: node.slug,
newerPost: newerPost,
olderPost: olderPost,
slug: post.slug,
recommendedPosts: recommendedPosts,
},
});
});
Expand Down