Skip to content
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
5 changes: 4 additions & 1 deletion www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"author": "Kyle Mathews <[email protected]>",
"dependencies": {
"bluebird": "^3.5.1",
"email-validator": "^1.1.1",
"gatsby": "^1.9.77",
"gatsby-image": "^1.0.14",
"gatsby-link": "^1.6.23",
Expand Down Expand Up @@ -38,6 +39,7 @@
"gatsby-transformer-yaml": "^1.5.7",
"graphql-request": "^1.4.0",
"gray-percentage": "^2.0.0",
"jsonp": "^0.2.1",
"limax": "^1.5.0",
"lodash": "^4.16.6",
"mitt": "^1.1.2",
Expand All @@ -47,7 +49,8 @@
"typeface-space-mono": "^0.0.40",
"typeface-spectral": "^0.0.40",
"typography-breakpoint-constants": "^0.15.10",
"typography-plugin-code": "^0.16.11"
"typography-plugin-code": "^0.16.11",
"validator": "^9.2.0"
},
"keywords": [
"gatsby"
Expand Down
160 changes: 160 additions & 0 deletions www/src/components/email-capture-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React from "react"
import { rhythm } from "../utils/typography"
import presets from "../utils/presets"
import jsonp from "jsonp"
import { validate } from "email-validator"
import { css } from "glamor"

let stripeAnimation = css.keyframes({
"0%": { backgroundPosition: `0 0` },
"100%": { backgroundPosition: `30px 60px` },
})

// Mailchimp endpoint
// From: https://us17.admin.mailchimp.com/lists/integration/embeddedcode?id=XXXXXX
// Where `XXXXXX` is the MC list ID
// Note: we change `/post` to `/post-json`
const MAILCHIMP_URL = `https://gatsbyjs.us17.list-manage.com/subscribe/post-json?u=1dc33f19eb115f7ebe4afe5ee&amp;id=f366064ba7`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should i make these env vars?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally this should be a prop passed into the component named mailChimpUrl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought about that too but after getting this merged, I was thinking of adding the form on other non-blog pages. then each parent component will need to pass the MAILCHIMP_URL as a prop, which gets super messy.

thoughts on env var? or just keeping this URL in this component? imagine having this component in a bunch of different templates in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just leave it hard-coded. It's not really a secret nor are we trying to make this component reusable for multiple lists atm. It's trivial to refactor later.


class EmailCaptureForm extends React.Component {
constructor() {
super()
this.state = {
email: ``,
}
}

// Update state each time user edits their email address
_handleEmailChange = e => {
this.setState({ email: e.target.value })
}

// Using jsonp, post to MC server & handle its response
_postEmailToMailchimp = url => {
// jsonp lib takes an `endpoint`, {options}, & callback
jsonp(url, { param: `c` }, (err, data) => {
// network failures, timeouts, etc
if (err) {
this.setState({
status: `error`,
msg: err,
})

// Mailchimp errors & failures
} else if (data.result !== `success`) {
this.setState({
status: `error`,
msg: data.msg,
})

// Posted email successfully to Mailchimp
} else {
this.setState({
status: `success`,
msg: data.msg,
})
}
})
}

// On form submit, validate email
// then jsonp to Mailchimp, and update state
_handleFormSubmit = e => {
e.preventDefault()
e.stopPropagation()

// If email is not valid, break early
if (!validate(this.state.email)) {
this.setState({
status: `error`,
msg: `"${this.state.email}" is not a valid email address`,
})
return
}

// Construct the url for our jsonp request
// Query params must be in CAPS
// Capture pathname for better email targeting
const url = `${MAILCHIMP_URL}
&EMAIL=${encodeURIComponent(this.state.email)}
&PATHNAME=${window.location.pathname}
`

this.setState(
{
msg: null,
status: `sending`,
},
// jsonp request as setState callback
this._postEmailToMailchimp(url)
)
}

render() {
return (
<div
css={{
borderTop: `2px solid ${presets.brandLight}`,
marginTop: rhythm(3),
paddingTop: `${rhythm(1)}`,
}}
>
{this.state.status === `success` ? (
<div>Thank you! Youʼll receive your first email shortly.</div>
) : (
<div>
<p>Enjoyed this post? Receive the next one in your inbox!</p>
<form
id="email-capture"
method="post"
noValidate
css={{ margin: 0 }}
>
<div>
<input
type="email"
name="email"
placeholder="[email protected]"
onChange={this._handleEmailChange}
css={{
padding: rhythm(1 / 2),
width: `250px`,
color: presets.bodyColor,
}}
/>
<button
type="submit"
onClick={this._handleFormSubmit}
css={{
borderRadius: `2px`,
border: `1px solid ${presets.brand}`,
cursor: `pointer`,
padding: rhythm(1 / 2),
margin: `${rhythm(1 / 2)} 0 0 ${rhythm(1 / 2)}`,
":hover": {
backgroundSize: `30px 30px`,
backgroundColor: presets.brand,
backgroundImage: `linear-gradient(45deg, rgba(0,0,0, 0.1) 25%, transparent 25%, transparent 50%, rgba(0,0,0, 0.1) 50%, rgba(0,0,0, 0.1) 75%, transparent 75%, transparent)`,
color: `#fff`,
animation: `${stripeAnimation} 2.8s linear infinite`,
},
}}
>
Subscribe
</button>
{this.state.status === `error` && (
<div
dangerouslySetInnerHTML={{ __html: this.state.msg }}
css={{ marginTop: `${rhythm(1 / 2)}` }}
/>
)}
</div>
</form>
</div>
)}
</div>
)
}
}

export default EmailCaptureForm
2 changes: 2 additions & 0 deletions www/src/templates/template-blog-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Img from "gatsby-image"
import presets from "../utils/presets"
import typography, { rhythm, scale, options } from "../utils/typography"
import Container from "../components/container"
import EmailCaptureForm from "../components/email-capture-form"

class BlogPostTemplate extends React.Component {
render() {
Expand Down Expand Up @@ -200,6 +201,7 @@ class BlogPostTemplate extends React.Component {
__html: this.props.data.markdownRemark.html,
}}
/>
<EmailCaptureForm />
</Container>
<div
css={{
Expand Down