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

[1.0] Add Hacker News data source plugin as well as a HN clone site #841

Merged
merged 4 commits into from
Apr 28, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ test/**/public
public/
node_modules/
.cache/
.netlify
4 changes: 4 additions & 0 deletions examples/hn/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ['react', 'es2015', 'stage-1'],
"plugins": ['add-module-exports']
}
3 changes: 3 additions & 0 deletions examples/hn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public
.intermediate-representation
node_modules
6 changes: 6 additions & 0 deletions examples/hn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Gatsbygram

https://gatsbygram.gatsbyjs.org/

Built with Gatsby 1.0 (Alpha 11) as a demo of Gatsby's new built-in
image processing capabilities.
44 changes: 44 additions & 0 deletions examples/hn/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module.exports = {
siteMetadata: {
title: `Gatsby Hacker News`,
},
plugins: [
/*
* Gatsby's data processing layer begins with “source”
* plugins. You can source data nodes from anywhere but
* most sites, like Gatsbygram, will include data from
* the filesystem so we start here with
* “gatsby-source-filesystem”.
*
* A site can have as many instances of
* gatsby-source-filesystem as you need. Each plugin
* instance is configured with a root path where it then
* recursively reads in files and adds them to the data
* tree.
*/
`gatsby-source-hacker-news`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Gatsby Hacker News`,
short_name: `Gatsby Hacker News`,
start_url: `/`,
background_color: `#f7f7f7`,
theme_color: `#191919`,
display: `minimal-ui`,
},
},
// This plugin generates a service worker and AppShell
// html file so the site works offline and is otherwise
// resistant to bad networks. Works with almost any
// site!
`gatsby-plugin-offline`,
// This plugin sets up Google Analytics for you.
{
resolve: `gatsby-plugin-google-analytics`,
options: {
// trackingId: `UA-91652198-1`,
},
},
],
}
63 changes: 63 additions & 0 deletions examples/hn/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const _ = require("lodash")
const Promise = require("bluebird")
const path = require("path")
const slash = require("slash")

// Implement the Gatsby API “createPages”. This is
// called after the Gatsby bootstrap is finished so you have
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
// queries against local Hacker News graphql schema. Think of
// it like the site has a built-in database constructed
// from the fetched data that you can run queries against.

// HnStory is a data node type created from the HN API “allHnStory” is a
// "connection" (a GraphQL convention for accessing a list of nodes) gives
// us an easy way to query all HnStory nodes.
graphql(
`
{
allHnStory(limit: 1000) {
edges {
node {
id
}
}
}
}
`
).then(result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}

// Create HN story pages.
const pageTemplate = path.resolve(`./src/templates/story.js`)
// We want to create a detailed page for each
// story page. We'll just use the HN story ID for the slug.
_.each(result.data.allHnStory.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "upsertPage"
// to interact with Gatsby.
upsertPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
// can query data specific to each page.
path: `/item/${edge.node.id}/`,
component: slash(pageTemplate),
context: {
id: edge.node.id,
},
})
})

resolve()
})
})
}
27 changes: 27 additions & 0 deletions examples/hn/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "gatsby-example-image-gallery",
"private": true,
"description": "Gatsby example site: image gallery",
"version": "1.0.0",
"author": "Kyle Mathews <[email protected]>",
"dependencies": {
"flat": "^2.0.1",
"gatsby": "1.0.0-alpha12-alpha.8370739a",
"gatsby-link": "1.0.0-alpha12-alpha.8370739a",
"gatsby-plugin-google-analytics": "1.0.0-alpha12-alpha.8370739a",
"gatsby-plugin-manifest": "1.0.0-alpha12-alpha.8370739a",
"gatsby-plugin-offline": "1.0.0-alpha12-alpha.8370739a",
"gatsby-source-hacker-news": "1.0.0-alpha12-alpha.8370739a",
"lodash": "^4.16.4",
"slash": "^1.0.0"
},
"keywords": [
"gatsby"
],
"license": "MIT",
"main": "n/a",
"scripts": {
"develop": "gatsby develop",
"build": "gatsby build"
}
}
154 changes: 154 additions & 0 deletions examples/hn/src/components/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React from "react"
import Modal from "react-modal"
import browserHistory from "react-router/lib/browserHistory"
import CaretRight from "react-icons/lib/fa/caret-right"
import CaretLeft from "react-icons/lib/fa/caret-left"
import Close from "react-icons/lib/md/close"
import findIndex from "lodash/findIndex"
import mousetrap from "mousetrap"

import { rhythm, scale } from "../utils/typography"

class GatsbyGramModal extends React.Component {
componentDidMount() {
mousetrap.bind("left", () => this.previous())
mousetrap.bind("right", () => this.next())
mousetrap.bind("spacebar", () => this.next())
}

componentWillUnmount() {
mousetrap.unbind("left")
mousetrap.unbind("right")
mousetrap.unbind("spacebar")
}

findCurrentIndex() {
let index
index = findIndex(this.props.edges, edge => {
return edge.node.id === this.props.location.pathname.split(`/`)[1]
})

return index
}

next(e) {
if (e) {
e.stopPropagation()
}
const currentIndex = this.findCurrentIndex()
if (currentIndex || currentIndex === 0) {
const edges = this.props.edges
let nextPost
// Wrap around if at end.
if (currentIndex + 1 === edges.length) {
nextPost = edges[0]
} else {
nextPost = edges[currentIndex + 1]
}
browserHistory.push(`/${nextPost.node.id}/`)
}
}

previous(e) {
if (e) {
e.stopPropagation()
}
const currentIndex = this.findCurrentIndex()
if (currentIndex || currentIndex === 0) {
const edges = this.props.edges
let previousPost
// Wrap around if at start.
if (currentIndex === 0) {
previousPost = edges.slice(-1)[0]
} else {
previousPost = edges[currentIndex - 1]
}
browserHistory.push(`/${previousPost.node.id}/`)
}
}

render() {
return (
<Modal
isOpen={this.props.isOpen}
onRequestClose={() => browserHistory.push(`/`)}
style={{
overlay: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.75)",
},
content: {
position: "absolute",
border: `none`,
background: `none`,
padding: 0,
top: 0,
bottom: 0,
right: 0,
left: 0,
overflow: "auto",
WebkitOverflowScrolling: "touch",
},
}}
contentLabel="Modal"
>
<div
onClick={() => browserHistory.push(`/`)}
css={{
display: `flex`,
position: `relative`,
height: `100vh`,
}}
>
<div
css={{
display: `flex`,
alignItems: `center`,
justifyItems: `center`,
maxWidth: rhythm(40.25), // Gets it right around Instagram's maxWidth.
margin: `auto`,
width: `100%`,
}}
>
<CaretLeft
css={{
cursor: `pointer`,
fontSize: `50px`,
color: `rgba(255,255,255,0.7)`,
userSelect: `none`,
}}
onClick={e => this.previous(e)}
/>
{this.props.children}
<CaretRight
css={{
cursor: `pointer`,
fontSize: `50px`,
color: `rgba(255,255,255,0.7)`,
userSelect: `none`,
}}
onClick={e => this.next(e)}
/>
</div>
<Close
onClick={() => browserHistory.push(`/`)}
css={{
cursor: `pointer`,
color: `rgba(255,255,255,0.8)`,
fontSize: `30px`,
position: `absolute`,
top: rhythm(1 / 4),
right: rhythm(1 / 4),
}}
/>
</div>
</Modal>
)
}
}

export default GatsbyGramModal
Loading