Skip to content

Commit

Permalink
[1.0] Add Hacker News data source plugin as well as a HN clone site (#…
Browse files Browse the repository at this point in the history
…841)

* Initial checkin of new Gatsby Hacker News example site

* Fix inferring that domains ending with .com are pointing to files

* Actually traverse up tree + detect potential problem with node links
  • Loading branch information
KyleAMathews authored Apr 28, 2017
1 parent f176527 commit 2370d50
Show file tree
Hide file tree
Showing 34 changed files with 9,182 additions and 7 deletions.
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

0 comments on commit 2370d50

Please sign in to comment.