diff --git a/www/package.json b/www/package.json index 106f4eee86da8..d3eb9323227b2 100644 --- a/www/package.json +++ b/www/package.json @@ -5,6 +5,7 @@ "author": "Kyle Mathews ", "dependencies": { "bluebird": "^3.5.1", + "email-validator": "^1.1.1", "gatsby": "^1.9.77", "gatsby-image": "^1.0.14", "gatsby-link": "^1.6.23", @@ -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", @@ -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" diff --git a/www/src/components/email-capture-form.js b/www/src/components/email-capture-form.js new file mode 100644 index 0000000000000..6a80a6c634386 --- /dev/null +++ b/www/src/components/email-capture-form.js @@ -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&id=f366064ba7` + +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 ( +
+ {this.state.status === `success` ? ( +
Thank you! Youʼll receive your first email shortly.
+ ) : ( +
+

Enjoyed this post? Receive the next one in your inbox!

+
+
+ + + {this.state.status === `error` && ( +
+ )} +
+ +
+ )} +
+ ) + } +} + +export default EmailCaptureForm diff --git a/www/src/templates/template-blog-post.js b/www/src/templates/template-blog-post.js index ffebb0cbae324..740f2802963ff 100644 --- a/www/src/templates/template-blog-post.js +++ b/www/src/templates/template-blog-post.js @@ -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() { @@ -200,6 +201,7 @@ class BlogPostTemplate extends React.Component { __html: this.props.data.markdownRemark.html, }} /> +