├── .prettierrc
├── src
├── images
│ ├── icon.png
│ ├── jane.jpg
│ ├── john.jpg
│ └── javascript.jpg
├── styles
│ ├── _card.scss
│ ├── _variables.scss
│ ├── index.scss
│ ├── _links.scss
│ ├── _sidebar.scss
│ ├── _base.scss
│ └── _footer.scss
├── pages
│ ├── posts
│ │ ├── 004-fourth-post.md
│ │ ├── 005-fifth-post.md
│ │ ├── 001-first-post.md
│ │ ├── 002-second-post.md
│ │ └── 003-third-post.md
│ ├── 404.js
│ ├── about.js
│ ├── team.js
│ └── index.js
├── util
│ ├── utilityFunctions.js
│ └── authors.js
├── templates
│ ├── tags-page.js
│ ├── tag-posts.js
│ ├── post-list.js
│ ├── author-posts.js
│ └── single-post.js
└── components
│ ├── Post.js
│ ├── layout.js
│ ├── header.js
│ ├── PaginationLinks.js
│ ├── Footer.js
│ ├── seo.js
│ └── Sidebar.js
├── gatsby-browser.js
├── gatsby-ssr.js
├── .travis.yml
├── LICENSE
├── README.md
├── .gitignore
├── gatsby-config.js
├── package.json
└── gatsby-node.js
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "trailingComma": "es5"
5 | }
6 |
--------------------------------------------------------------------------------
/src/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hidjou/classsed-gatsby-blog/HEAD/src/images/icon.png
--------------------------------------------------------------------------------
/src/images/jane.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hidjou/classsed-gatsby-blog/HEAD/src/images/jane.jpg
--------------------------------------------------------------------------------
/src/images/john.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hidjou/classsed-gatsby-blog/HEAD/src/images/john.jpg
--------------------------------------------------------------------------------
/src/images/javascript.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hidjou/classsed-gatsby-blog/HEAD/src/images/javascript.jpg
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Browser APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/browser-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/src/styles/_card.scss:
--------------------------------------------------------------------------------
1 | .card {
2 | margin-bottom: 30px;
3 | }
4 | ul.post-tags {
5 | padding: 0;
6 | display: inline-flex;
7 | list-style: none;
8 |
9 | li {
10 | margin-right: 10px;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | $site-font: 'Raleway', sans-serif;
4 |
5 | $light-gray: rgb(240, 240, 240);
6 | $dark-gray: rgb(80, 80, 80);
7 | $white: white;
8 |
--------------------------------------------------------------------------------
/src/pages/posts/004-fourth-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Fourth Post - CSS'
3 | date: 2018-12-04 07:00:00
4 | author: 'Jane Doe'
5 | image: ../../images/javascript.jpg
6 | tags:
7 | - tutorial
8 | ---
9 |
10 | New post on Netlify
11 |
--------------------------------------------------------------------------------
/src/pages/posts/005-fifth-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Fifth Post - CSS'
3 | date: 2018-12-05 07:00:00
4 | author: 'John Doe'
5 | image: ../../images/javascript.jpg
6 | tags:
7 | - tutorial
8 | ---
9 |
10 | New post on TravisCI
11 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | // Bootstrap scss
2 | @import '../../node_modules/bootstrap/scss/bootstrap.scss';
3 |
4 | @import './variables';
5 | @import './base';
6 | @import './footer';
7 | @import './card';
8 | @import './links';
9 | @import './sidebar';
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 8.14.0
4 | install: yarn
5 | script: gatsby build
6 | deploy:
7 | provider: s3
8 | access_key_id: $AWS_KEY
9 | secret_access_key: $AWS_SECRET
10 | bucket: 'gatsby-test-deploy'
11 | skip_cleanup: true
12 | acl: public_read
13 | local_dir: public
14 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'gatsby'
3 | import Layout from '../components/layout'
4 | import SEO from '../components/seo'
5 |
6 | const NotFoundPage = () => (
7 |
8 |
9 |
10 | Go home
11 |
12 |
13 | )
14 |
15 | export default NotFoundPage
16 |
--------------------------------------------------------------------------------
/src/pages/posts/001-first-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'First Post - Introduction'
3 | date: 2018-12-01 07:00:00
4 | author: 'John Doe'
5 | image: ../../images/javascript.jpg
6 | tags:
7 | - code
8 | ---
9 |
10 | Welcome to Code Blog, I hope you enjoy the content, Welcome to Code Blog, I hope you enjoy the content, Welcome to Code Blog, I hope you enjoy the content, Welcome to Code Blog, I hope you enjoy the content, Welcome to Code Blog, I hope you enjoy the content.
11 |
--------------------------------------------------------------------------------
/src/pages/posts/002-second-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Second Post - HTML'
3 | date: 2018-12-02 07:00:00
4 | author: 'Jane Doe'
5 | image: ../../images/javascript.jpg
6 | tags:
7 | - code
8 | - design
9 | ---
10 |
11 | Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5, Today, we are gonna learn about HTML5.
12 |
--------------------------------------------------------------------------------
/src/util/utilityFunctions.js:
--------------------------------------------------------------------------------
1 | const slugify = function(text) {
2 | return text
3 | .toString()
4 | .toLowerCase()
5 | .replace(/\s+/g, '-') // Replace spaces with -
6 | .replace(/[^\w-]+/g, '') // Remove all non-word chars
7 | .replace(/--+/g, '-') // Replace multiple - with single -
8 | .replace(/^-+/, '') // Trim - from start of text
9 | .replace(/-+$/, '') // Trim - from end of text
10 | }
11 |
12 | // Web Design => web-design
13 |
14 | module.exports = { slugify }
15 |
--------------------------------------------------------------------------------
/src/styles/_links.scss:
--------------------------------------------------------------------------------
1 | .social-share-links {
2 | ul {
3 | display: inline-flex;
4 | margin: 20px auto;
5 |
6 | li {
7 | margin: auto 20px;
8 |
9 | a {
10 | opacity: 0.6;
11 | transition: all 0.1s ease-in;
12 | &:hover {
13 | opacity: 1;
14 | }
15 | &.facebook {
16 | color: rgb(66, 103, 178);
17 | }
18 | &.twitter {
19 | color: rgb(56, 161, 243);
20 | }
21 | &.google {
22 | color: rgb(221, 75, 57);
23 | }
24 | &.linkedin {
25 | color: rgb(0, 119, 181);
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/styles/_sidebar.scss:
--------------------------------------------------------------------------------
1 | .author-social-links {
2 | ul {
3 | display: inline-flex;
4 |
5 | li {
6 | margin: auto 7px;
7 |
8 | a {
9 | transition: all 0.1s ease-out;
10 | color: $dark-gray;
11 |
12 | &.instagram:hover {
13 | color: #e95950;
14 | }
15 | &.facebook:hover {
16 | color: rgb(66, 103, 178);
17 | }
18 | &.twitter:hover {
19 | color: rgb(56, 161, 243);
20 | }
21 | &.google:hover {
22 | color: rgb(221, 75, 57);
23 | }
24 | &.linkedin:hover {
25 | color: rgb(0, 119, 181);
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/pages/about.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import SEO from '../components/seo'
4 |
5 | const AboutPage = () => (
6 |
7 |
8 |
9 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Minus quibusdam
10 | pariatur magnam nulla quis nobis rerum vitae in delectus modi
11 | exercitationem amet quasi, distinctio illo blanditiis, adipisci, odio unde
12 | minima numquam architecto! Odit sequi facere sit odio maiores possimus
13 | ratione!
14 |
15 |
16 | )
17 |
18 | export default AboutPage
19 |
--------------------------------------------------------------------------------
/src/styles/_base.scss:
--------------------------------------------------------------------------------
1 | .container#content {
2 | margin-top: 60px;
3 | }
4 | body {
5 | background-color: $light-gray;
6 | font-family: $site-font;
7 | }
8 | a {
9 | color: #0056b3;
10 | &:hover {
11 | color: #007bff;
12 | text-decoration: none;
13 | }
14 | }
15 | h1 {
16 | margin-bottom: 20px;
17 | text-align: center;
18 | }
19 | .navbar {
20 | background-color: white;
21 | // border-right: 1px solid rgba(0, 0, 0, 0.4);
22 | // border-left: 1px solid rgba(0, 0, 0, 0.4);
23 | border-bottom: 1px solid rgba(0, 0, 0, 0.4);
24 | line-height: 1;
25 | }
26 | .navbar-light .navbar-nav .nav-link,
27 | .navbar-light .navbar-brand {
28 | color: #0056b3;
29 | &:hover {
30 | color: #007bff;
31 | }
32 | }
33 | ul {
34 | list-style: none;
35 | padding: 0;
36 | }
37 |
--------------------------------------------------------------------------------
/src/templates/tags-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import SEO from '../components/seo'
4 | import { Badge, Button } from 'reactstrap'
5 | import { slugify } from '../util/utilityFunctions'
6 |
7 | const tagsPage = ({ pageContext }) => {
8 | const { tags, tagPostCounts } = pageContext
9 | return (
10 |
11 |
12 |
13 | {tags.map(tag => (
14 |
15 |
16 | {tag} {tagPostCounts[tag]}
17 |
18 |
19 | ))}
20 |
21 |
22 | )
23 | }
24 |
25 | export default tagsPage
26 |
--------------------------------------------------------------------------------
/src/styles/_footer.scss:
--------------------------------------------------------------------------------
1 | .site-footer {
2 | background-color: $white;
3 | margin-top: 20px;
4 | padding: 10px;
5 | text-align: center;
6 |
7 | .footer-social-links {
8 | text-align: center;
9 |
10 | ul.social-links-list {
11 | li {
12 | margin-right: 25px;
13 | display: inline-block;
14 |
15 | a {
16 | color: $dark-gray;
17 |
18 | &.instagram:hover {
19 | color: #e95950;
20 | }
21 | &.facebook:hover {
22 | color: rgb(66, 103, 178);
23 | }
24 | &.twitter:hover {
25 | color: rgb(56, 161, 243);
26 | }
27 | &.google:hover {
28 | color: rgb(221, 75, 57);
29 | }
30 | &.linkedin:hover {
31 | color: rgb(0, 119, 181);
32 | }
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/util/authors.js:
--------------------------------------------------------------------------------
1 | const authors = [
2 | {
3 | name: 'John Doe',
4 | imageUrl: 'john.jpg',
5 | bio:
6 | 'John has been a front-end and UI designer fpr over 10 years, he is a brilliant artist',
7 | facebook: 'https://www.facebook.com/',
8 | twitter: 'https://www.twitter.com/',
9 | instagram: 'https://www.instagram.com/',
10 | google: 'https://www.google.com/',
11 | linkedin: 'https://www.linkedin.com/',
12 | },
13 | {
14 | name: 'Jane Doe',
15 | imageUrl: 'jane.jpg',
16 | bio:
17 | 'Jane is a back-end developer, she specializes in security and her favourite stack is the MERN stack',
18 | facebook: 'https://www.facebook.com/',
19 | twitter: 'https://www.twitter.com/',
20 | instagram: 'https://www.instagram.com/',
21 | google: 'https://www.google.com/',
22 | linkedin: 'https://www.linkedin.com/',
23 | },
24 | ]
25 |
26 | module.exports = authors
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 gatsbyjs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Gatsby Blog Tutorial
9 |
10 |
11 | This is the complete code for the Gatsby tutorial I published on my Youtube channel
12 | [Classsed](https://www.youtube.com/channel/UC2-slOJImuSc20Drbf88qvg)
13 |
14 | Full tutorial series: https://www.youtube.com/watch?v=lvq2nc3WkZU&list=PLMhAeHCz8S3_x-jXerCYnl7jftCSxQkPV
15 |
16 | ## Guide
17 |
18 | 1. **Clone/Download the code from this repo**
19 |
20 | ```sh
21 | git clone git@github.com:hidjou/gatsby-blog-tutorial.git
22 | ```
23 |
24 | 1. **Install dependencies**
25 |
26 | ```sh
27 | cd gatsby-blog-tutorial
28 | npm install
29 | ```
30 |
31 | or
32 |
33 | ```sh
34 | cd gatsby-blog-tutorial
35 | yarn install
36 | ```
37 |
38 | 1. ** 🚀 Launch project**
39 |
40 | ```sh
41 | gatsby develop
42 | ```
43 |
44 | The blog is now running at `http://localhost:8000`!
45 |
46 | ## Deployed instance
47 |
48 | https://cocky-elion-477c3b.netlify.com/
49 |
50 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
--------------------------------------------------------------------------------
/src/components/Post.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Badge,
4 | Card,
5 | CardTitle,
6 | CardText,
7 | CardSubtitle,
8 | CardBody,
9 | } from 'reactstrap'
10 | import { Link } from 'gatsby'
11 | import Img from 'gatsby-image'
12 | import { slugify } from '../util/utilityFunctions'
13 |
14 | const Post = ({ title, author, slug, date, body, fluid, tags }) => (
15 |
16 |
17 |
18 |
19 |
20 |
21 | {title}
22 |
23 |
24 | {date} by{' '}
25 | {author}
26 |
27 | {body}
28 |
29 | {tags.map(tag => (
30 |
31 |
32 |
33 | {tag}
34 |
35 |
36 |
37 | ))}
38 |
39 |
43 | Read more
44 |
45 |
46 |
47 | )
48 |
49 | export default Post
50 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `Code Blog`,
4 | description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
5 | author: `@gatsbyjs`,
6 | },
7 | plugins: [
8 | `gatsby-plugin-react-helmet`,
9 | 'gatsby-plugin-sass',
10 | 'gatsby-plugin-catch-links',
11 | 'gatsby-transformer-remark',
12 | {
13 | resolve: `gatsby-source-filesystem`,
14 | options: {
15 | name: `images`,
16 | path: `${__dirname}/src/images`,
17 | },
18 | },
19 | {
20 | resolve: `gatsby-source-filesystem`,
21 | options: {
22 | name: `pages`,
23 | path: `${__dirname}/src/pages`,
24 | },
25 | },
26 | `gatsby-transformer-sharp`,
27 | `gatsby-plugin-sharp`,
28 | {
29 | resolve: `gatsby-plugin-manifest`,
30 | options: {
31 | name: `gatsby-starter-default`,
32 | short_name: `starter`,
33 | start_url: `/`,
34 | background_color: `#663399`,
35 | theme_color: `#663399`,
36 | display: `minimal-ui`,
37 | icon: `src/images/icon.png`, // This path is relative to the root of the site.
38 | },
39 | },
40 | // this (optional) plugin enables Progressive Web App + Offline functionality
41 | // To learn more, visit: https://gatsby.app/offline
42 | // 'gatsby-plugin-offline',
43 | ],
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { StaticQuery, graphql } from 'gatsby'
4 |
5 | import Header from './header'
6 | import Footer from './Footer'
7 | import Sidebar from './Sidebar'
8 | import '../styles/index.scss'
9 |
10 | import { Row, Col } from 'reactstrap'
11 |
12 | const Layout = ({ authorImageFluid, children, pageTitle, postAuthor }) => (
13 | (
24 | <>
25 |
31 |
32 |
33 |
{pageTitle}
34 |
35 | {children}
36 |
37 |
38 |
39 |
40 |
41 |
42 | >
43 | )}
44 | />
45 | )
46 |
47 | Layout.propTypes = {
48 | children: PropTypes.node.isRequired,
49 | }
50 |
51 | export default Layout
52 |
--------------------------------------------------------------------------------
/src/components/header.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | import {
5 | Collapse,
6 | Navbar,
7 | NavbarToggler,
8 | NavbarBrand,
9 | Nav,
10 | NavItem,
11 | NavLink,
12 | } from 'reactstrap'
13 |
14 | class Header extends React.Component {
15 | constructor(props) {
16 | super(props)
17 |
18 | this.toggle = this.toggle.bind(this)
19 | this.state = {
20 | isOpen: false,
21 | }
22 | }
23 | toggle() {
24 | this.setState({
25 | isOpen: !this.state.isOpen,
26 | })
27 | }
28 | render() {
29 | return (
30 |
31 |
32 |
33 | {this.props.siteTitle}
34 |
35 |
36 |
37 |
38 | Team
39 |
40 |
41 | Tags
42 |
43 |
44 | About
45 |
46 |
47 |
48 |
49 |
50 |
51 | )
52 | }
53 | }
54 |
55 | Header.propTypes = {
56 | siteTitle: PropTypes.string,
57 | }
58 |
59 | Header.defaultProps = {
60 | siteTitle: ``,
61 | }
62 |
63 | export default Header
64 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.1.0",
6 | "author": "Kyle Mathews ",
7 | "dependencies": {
8 | "bootstrap": "^4.2.1",
9 | "disqus-react": "^1.0.5",
10 | "gatsby": "^2.0.75",
11 | "gatsby-image": "^2.0.20",
12 | "gatsby-plugin-catch-links": "^2.0.9",
13 | "gatsby-plugin-manifest": "^2.0.9",
14 | "gatsby-plugin-offline": "^2.0.16",
15 | "gatsby-plugin-react-helmet": "^3.0.2",
16 | "gatsby-plugin-sass": "^2.0.7",
17 | "gatsby-plugin-sharp": "^2.0.14",
18 | "gatsby-source-filesystem": "^2.0.8",
19 | "gatsby-transformer-remark": "^2.1.17",
20 | "gatsby-transformer-sharp": "^2.1.8",
21 | "lodash": "^4.17.11",
22 | "node-sass": "^4.11.0",
23 | "prop-types": "^15.6.2",
24 | "react": "^16.6.3",
25 | "react-dom": "^16.6.3",
26 | "react-helmet": "^5.2.0",
27 | "reactstrap": "^6.5.0"
28 | },
29 | "keywords": [
30 | "gatsby"
31 | ],
32 | "license": "MIT",
33 | "scripts": {
34 | "build": "gatsby build",
35 | "develop": "gatsby develop",
36 | "start": "npm run develop",
37 | "format": "prettier --write \"src/**/*.js\"",
38 | "test": "echo \"Write tests! -> https://gatsby.app/unit-testing\""
39 | },
40 | "devDependencies": {
41 | "prettier": "^1.15.2"
42 | },
43 | "repository": {
44 | "type": "git",
45 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
46 | },
47 | "bugs": {
48 | "url": "https://github.com/gatsbyjs/gatsby/issues"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/PaginationLinks.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pagination, PaginationItem, PaginationLink } from 'reactstrap'
3 |
4 | const PaginationLinks = ({ currentPage, numberOfPages }) => {
5 | const isFirst = currentPage === 1
6 | const isLast = currentPage === numberOfPages
7 | const previousPage =
8 | currentPage - 1 === 1 ? '/' : '/page/' + (currentPage - 1).toString()
9 | const nextPage = '/page/' + (currentPage + 1).toString()
10 | return (
11 |
12 | {isFirst ? (
13 |
14 |
15 |
16 | ) : (
17 |
18 |
19 |
20 | )}
21 | {Array.from({ length: numberOfPages }, (_, i) =>
22 | currentPage === i + 1 ? (
23 |
24 |
25 | {i + 1}
26 |
27 |
28 | ) : (
29 |
30 |
31 | {i + 1}
32 |
33 |
34 | )
35 | )}
36 | {isLast ? (
37 |
38 |
39 |
40 | ) : (
41 |
42 |
43 |
44 | )}
45 |
46 | )
47 | }
48 |
49 | export default PaginationLinks
50 |
--------------------------------------------------------------------------------
/src/templates/tag-posts.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { graphql } from 'gatsby'
3 | import Layout from '../components/layout'
4 | import Post from '../components/Post'
5 |
6 | const tagPosts = ({ data, pageContext }) => {
7 | const { tag } = pageContext
8 | const { totalCount } = data.allMarkdownRemark
9 | const pageHeader = `${totalCount} post${
10 | totalCount === 1 ? '' : 's'
11 | } tagged with "${tag}"`
12 |
13 | return (
14 |
15 | {data.allMarkdownRemark.edges.map(({ node }) => (
16 |
26 | ))}
27 |
28 | )
29 | }
30 |
31 | export const tagQuery = graphql`
32 | query($tag: String!) {
33 | allMarkdownRemark(
34 | sort: { fields: [frontmatter___date], order: DESC }
35 | filter: { frontmatter: { tags: { in: [$tag] } } }
36 | ) {
37 | totalCount
38 | edges {
39 | node {
40 | id
41 | frontmatter {
42 | title
43 | date(formatString: "MMMM Do YYYY")
44 | author
45 | tags
46 | image {
47 | childImageSharp {
48 | fluid(maxWidth: 650, maxHeight: 371) {
49 | ...GatsbyImageSharpFluid
50 | }
51 | }
52 | }
53 | }
54 | fields {
55 | slug
56 | }
57 | excerpt
58 | }
59 | }
60 | }
61 | }
62 | `
63 |
64 | export default tagPosts
65 |
--------------------------------------------------------------------------------
/src/templates/post-list.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import Post from '../components/Post'
4 | import { graphql } from 'gatsby'
5 | import PaginationLinks from '../components/PaginationLinks'
6 |
7 | const postList = props => {
8 | const posts = props.data.allMarkdownRemark.edges
9 | const { currentPage, numberOfPages } = props.pageContext
10 |
11 | return (
12 |
13 | {posts.map(({ node }) => (
14 |
24 | ))}
25 |
29 |
30 | )
31 | }
32 |
33 | export const postListQuery = graphql`
34 | query postListQuery($skip: Int!, $limit: Int!) {
35 | allMarkdownRemark(
36 | sort: { fields: [frontmatter___date], order: DESC }
37 | limit: $limit
38 | skip: $skip
39 | ) {
40 | edges {
41 | node {
42 | id
43 | frontmatter {
44 | title
45 | date(formatString: "MMMM Do YYYY")
46 | author
47 | tags
48 | image {
49 | childImageSharp {
50 | fluid(maxWidth: 650, maxHeight: 371) {
51 | ...GatsbyImageSharpFluid
52 | }
53 | }
54 | }
55 | }
56 | fields {
57 | slug
58 | }
59 | excerpt
60 | }
61 | }
62 | }
63 | }
64 | `
65 |
66 | export default postList
67 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Footer = () => (
4 |
5 |
Code Blog
6 |
Follow us on social media
7 |
61 |
62 | )
63 |
64 | export default Footer
65 |
--------------------------------------------------------------------------------
/src/pages/team.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import SEO from '../components/seo'
4 | import authors from '../util/authors'
5 | import { Button, Card, CardText, CardBody, CardTitle, Row } from 'reactstrap'
6 | import JohnImage from '../images/john.jpg'
7 | import JaneImage from '../images/jane.jpg'
8 | import { slugify } from '../util/utilityFunctions'
9 |
10 | const TeamPage = () => (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {authors[0].name}
21 | {authors[0].bio}
22 |
27 | View posts
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {authors[1].name}
41 | {authors[1].bio}
42 |
47 | View posts
48 |
49 |
50 |
51 |
52 |
53 |
54 | )
55 |
56 | export default TeamPage
57 |
--------------------------------------------------------------------------------
/src/pages/posts/003-third-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Third Post - CSS'
3 | date: 2018-12-03 07:00:00
4 | author: 'John Doe'
5 | image: ../../images/javascript.jpg
6 | tags:
7 | - design
8 | - tutorial
9 | ---
10 |
11 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
12 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
13 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
14 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
15 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
16 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
17 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
18 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
19 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
20 | Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys, Let dive into CSS guys.
21 |
--------------------------------------------------------------------------------
/src/templates/author-posts.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import Post from '../components/Post'
4 | import { graphql } from 'gatsby'
5 | import authors from '../util/authors'
6 |
7 | const authorPosts = ({ data, pageContext }) => {
8 | const { totalCount } = data.allMarkdownRemark
9 | const author = authors.find(x => x.name === pageContext.authorName)
10 | const pageHeader = `${totalCount} Posts by: ${pageContext.authorName}`
11 |
12 | return (
13 |
18 | {data.allMarkdownRemark.edges.map(({ node }) => (
19 |
29 | ))}
30 |
31 | )
32 | }
33 |
34 | export const authorQuery = graphql`
35 | query($authorName: String!, $imageUrl: String!) {
36 | allMarkdownRemark(
37 | sort: { fields: [frontmatter___date], order: DESC }
38 | filter: { frontmatter: { author: { eq: $authorName } } }
39 | ) {
40 | totalCount
41 | edges {
42 | node {
43 | id
44 | frontmatter {
45 | title
46 | date(formatString: "MMMM Do YYYY")
47 | author
48 | tags
49 | image {
50 | childImageSharp {
51 | fluid(maxWidth: 650) {
52 | ...GatsbyImageSharpFluid
53 | }
54 | }
55 | }
56 | }
57 | fields {
58 | slug
59 | }
60 | excerpt
61 | }
62 | }
63 | }
64 | file(relativePath: { eq: $imageUrl }) {
65 | childImageSharp {
66 | fluid(maxWidth: 300) {
67 | ...GatsbyImageSharpFluid
68 | }
69 | }
70 | }
71 | }
72 | `
73 |
74 | export default authorPosts
75 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import SEO from '../components/seo'
4 | import { graphql, StaticQuery } from 'gatsby'
5 | import Post from '../components/Post'
6 | import PaginationLinks from '../components/PaginationLinks'
7 |
8 | const IndexPage = () => {
9 | const postsPerPage = 2
10 | let numberOfPages
11 | return (
12 |
13 |
14 | {
17 | numberOfPages = Math.ceil(
18 | data.allMarkdownRemark.totalCount / postsPerPage
19 | )
20 | return (
21 |
22 | {data.allMarkdownRemark.edges.map(({ node }) => (
23 |
33 | ))}
34 |
35 |
36 | )
37 | }}
38 | />
39 |
40 | )
41 | }
42 |
43 | const indexQuery = graphql`
44 | query indexQuery {
45 | allMarkdownRemark(
46 | sort: { fields: [frontmatter___date], order: DESC }
47 | limit: 2
48 | ) {
49 | totalCount
50 | edges {
51 | node {
52 | id
53 | frontmatter {
54 | title
55 | date(formatString: "MMM Do YYYY")
56 | author
57 | tags
58 | image {
59 | childImageSharp {
60 | fluid(maxWidth: 600) {
61 | ...GatsbyImageSharpFluid
62 | }
63 | }
64 | }
65 | }
66 | fields {
67 | slug
68 | }
69 | excerpt
70 | }
71 | }
72 | }
73 | }
74 | `
75 |
76 | export default IndexPage
77 |
--------------------------------------------------------------------------------
/src/components/seo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Helmet from 'react-helmet'
4 | import { StaticQuery, graphql } from 'gatsby'
5 |
6 | function SEO({
7 | author,
8 | description,
9 | lang,
10 | meta,
11 | keywords,
12 | title,
13 | url,
14 | pathname,
15 | }) {
16 | return (
17 | {
20 | return (
21 | 0
67 | ? {
68 | name: 'keywords',
69 | content: keywords.join(', '),
70 | }
71 | : []
72 | )
73 | .concat(meta)}
74 | />
75 | )
76 | }}
77 | />
78 | )
79 | }
80 |
81 | SEO.defaultProps = {
82 | lang: 'en',
83 | meta: [],
84 | keywords: [],
85 | }
86 |
87 | SEO.propTypes = {
88 | description: PropTypes.string,
89 | lang: PropTypes.string,
90 | meta: PropTypes.array,
91 | keywords: PropTypes.arrayOf(PropTypes.string),
92 | title: PropTypes.string.isRequired,
93 | }
94 |
95 | export default SEO
96 |
97 | export const detailsQuery = graphql`
98 | query DefaultSEOQuery {
99 | site {
100 | siteMetadata {
101 | title
102 | description
103 | }
104 | }
105 | }
106 | `
107 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const { slugify } = require('./src/util/utilityFunctions')
3 | const authors = require('./src/util/authors')
4 | const _ = require('lodash')
5 |
6 | exports.onCreateNode = ({ node, actions }) => {
7 | const { createNodeField } = actions
8 | if (node.internal.type === 'MarkdownRemark') {
9 | const slugFromTitle = slugify(node.frontmatter.title)
10 | createNodeField({
11 | node,
12 | name: 'slug',
13 | value: slugFromTitle,
14 | })
15 | }
16 | }
17 |
18 | exports.createPages = async ({ actions, graphql }) => {
19 | const { createPage } = actions
20 |
21 | // Page templates
22 | const templates = {
23 | post: path.resolve('src/templates/single-post.js'),
24 | postList: path.resolve('src/templates/post-list.js'),
25 | tag: path.resolve('src/templates/tag-posts.js'),
26 | tagsPage: path.resolve('src/templates/tags-page.js'),
27 | authorPosts: path.resolve('src/templates/author-posts.js'),
28 | }
29 |
30 | const res = await graphql(`
31 | {
32 | allMarkdownRemark {
33 | edges {
34 | node {
35 | frontmatter {
36 | author
37 | tags
38 | }
39 | fields {
40 | slug
41 | }
42 | }
43 | }
44 | }
45 | }
46 | `)
47 |
48 | if (res.errors) return Promise.reject(res.errors)
49 |
50 | // Extracting all posts from res
51 | const posts = res.data.allMarkdownRemark.edges
52 |
53 | // Create single post pages
54 | posts.forEach(({ node }) => {
55 | createPage({
56 | path: node.fields.slug,
57 | component: templates.post,
58 | context: {
59 | // Passing slug for template to use to fetch the post
60 | slug: node.fields.slug,
61 | // Find author imageUrl from author array and pass it to template
62 | imageUrl: authors.find(x => x.name === node.frontmatter.author)
63 | .imageUrl,
64 | },
65 | })
66 | })
67 |
68 | // Create posts pagination pages
69 | const postsPerPage = 2
70 | const numberOfPages = Math.ceil(posts.length / postsPerPage)
71 |
72 | Array.from({ length: numberOfPages }).forEach((_, index) => {
73 | const isFirstPage = index === 0
74 | const currentPage = index + 1
75 |
76 | // Skip first page because of index.js
77 | if (isFirstPage) return
78 |
79 | createPage({
80 | path: `/page/${currentPage}`,
81 | component: templates.postList,
82 | context: {
83 | limit: postsPerPage,
84 | skip: index * postsPerPage,
85 | numberOfPages: numberOfPages,
86 | currentPage: currentPage,
87 | },
88 | })
89 | })
90 | // Get all tags
91 | let tags = []
92 | _.each(posts, edge => {
93 | if (_.get(edge, 'node.frontmatter.tags')) {
94 | tags = tags.concat(edge.node.frontmatter.tags)
95 | }
96 | })
97 |
98 | let tagPostCounts = {} // { tutorial: 2, design: 1}
99 | tags.forEach(tag => {
100 | // Or 0 cause it might not exist yet
101 | tagPostCounts[tag] = (tagPostCounts[tag] || 0) + 1
102 | })
103 |
104 | // Remove duplicates
105 | tags = _.uniq(tags)
106 |
107 | // Tags page (all tags)
108 | createPage({
109 | path: '/tags',
110 | component: templates.tagsPage,
111 | context: {
112 | tags,
113 | tagPostCounts,
114 | },
115 | })
116 |
117 | // Tag posts pages
118 | tags.forEach(tag => {
119 | createPage({
120 | path: `/tag/${_.kebabCase(tag)}`,
121 | component: templates.tag,
122 | context: {
123 | tag,
124 | },
125 | })
126 | })
127 |
128 | // Create author posts pages
129 | authors.forEach(author => {
130 | createPage({
131 | path: `/author/${slugify(author.name)}`,
132 | component: templates.authorPosts,
133 | context: {
134 | authorName: author.name,
135 | imageUrl: author.imageUrl,
136 | },
137 | })
138 | })
139 | }
140 |
--------------------------------------------------------------------------------
/src/templates/single-post.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Layout from '../components/layout'
3 | import { graphql, Link } from 'gatsby'
4 | import SEO from '../components/seo'
5 | import { Badge, Card, CardBody, CardSubtitle } from 'reactstrap'
6 | import Img from 'gatsby-image'
7 | import { slugify } from '../util/utilityFunctions'
8 | import authors from '../util/authors'
9 | import { DiscussionEmbed } from 'disqus-react'
10 |
11 | const SinglePost = ({ data, pageContext, location }) => {
12 | const post = data.markdownRemark.frontmatter
13 | const author = authors.find(x => x.name === post.author)
14 |
15 | const baseUrl = 'https://gatsbytutorial.co.uk/'
16 |
17 | const disqusShortname = 'https-gatsbytutorial-co-uk'
18 | const disqusConfig = {
19 | identifier: data.markdownRemark.id,
20 | title: post.title,
21 | url: baseUrl + pageContext.slug,
22 | }
23 |
24 | return (
25 |
30 |
38 |
39 |
43 |
44 |
45 | {post.date} by{' '}
46 | {post.author}
47 |
48 |
49 |
50 | {post.tags.map(tag => (
51 |
52 |
53 | {tag}
54 |
55 |
56 | ))}
57 |
58 |
59 |
60 | Share this post
61 |
125 |
126 |
127 | )
128 | }
129 |
130 | export const postQuery = graphql`
131 | query blogPostBySlug($slug: String!, $imageUrl: String!) {
132 | markdownRemark(fields: { slug: { eq: $slug } }) {
133 | id
134 | html
135 | frontmatter {
136 | title
137 | author
138 | date(formatString: "MMM Do YYYY")
139 | tags
140 | image {
141 | childImageSharp {
142 | fluid(maxWidth: 700) {
143 | ...GatsbyImageSharpFluid
144 | }
145 | }
146 | }
147 | }
148 | }
149 | file(relativePath: { eq: $imageUrl }) {
150 | childImageSharp {
151 | fluid(maxWidth: 300) {
152 | ...GatsbyImageSharpFluid
153 | }
154 | }
155 | }
156 | }
157 | `
158 |
159 | export default SinglePost
160 |
--------------------------------------------------------------------------------
/src/components/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Card,
4 | CardTitle,
5 | CardBody,
6 | CardText,
7 | Form,
8 | FormGroup,
9 | Input,
10 | } from 'reactstrap'
11 | import { graphql, StaticQuery, Link } from 'gatsby'
12 | import Img from 'gatsby-image'
13 |
14 | const Sidebar = ({ author, authorFluid }) => (
15 |
16 | {author && (
17 |
18 |
19 |
20 |
21 | {author.name}
22 |
23 | {author.bio}
24 |
78 |
79 |
80 | )}
81 |
82 |
83 |
84 | Newsletter
85 |
86 |
98 |
99 |
100 |
101 |
102 |
103 | Advertisement
104 |
105 |
110 |
111 |
112 |
113 |
114 |
115 | Recent Posts
116 |
117 | (
120 |
121 | {data.allMarkdownRemark.edges.map(({ node }) => (
122 |
123 |
124 |
128 |
129 |
130 |
131 |
132 | {node.frontmatter.title}
133 |
134 |
135 |
136 |
137 | ))}
138 |
139 | )}
140 | />
141 |
142 |
143 |
144 | )
145 |
146 | const sidebarQuery = graphql`
147 | query sidebarQuery {
148 | allMarkdownRemark(
149 | sort: { fields: [frontmatter___date], order: DESC }
150 | limit: 3
151 | ) {
152 | edges {
153 | node {
154 | id
155 | frontmatter {
156 | title
157 | image {
158 | childImageSharp {
159 | fluid(maxWidth: 300) {
160 | ...GatsbyImageSharpFluid
161 | }
162 | }
163 | }
164 | }
165 | fields {
166 | slug
167 | }
168 | }
169 | }
170 | }
171 | }
172 | `
173 |
174 | export default Sidebar
175 |
--------------------------------------------------------------------------------