Skip to content

Commit bdb5a00

Browse files
benjaminhoffmanKyleAMathews
authored andcommitted
Adds email capture to bottom of blog (#3333)
* Adds email capture to bottom of blog * Adds email capture to bottom of blog * Adds Mailchimp functionality * Refactors postEmailToMailchimp method, updates cc * updates rhythm css, installs & uses validator module * Fiddle with design
1 parent 2d37099 commit bdb5a00

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

www/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"author": "Kyle Mathews <[email protected]>",
66
"dependencies": {
77
"bluebird": "^3.5.1",
8+
"email-validator": "^1.1.1",
89
"gatsby": "^1.9.77",
910
"gatsby-image": "^1.0.14",
1011
"gatsby-link": "^1.6.23",
@@ -38,6 +39,7 @@
3839
"gatsby-transformer-yaml": "^1.5.7",
3940
"graphql-request": "^1.4.0",
4041
"gray-percentage": "^2.0.0",
42+
"jsonp": "^0.2.1",
4143
"limax": "^1.5.0",
4244
"lodash": "^4.16.6",
4345
"mitt": "^1.1.2",
@@ -47,7 +49,8 @@
4749
"typeface-space-mono": "^0.0.40",
4850
"typeface-spectral": "^0.0.40",
4951
"typography-breakpoint-constants": "^0.15.10",
50-
"typography-plugin-code": "^0.16.11"
52+
"typography-plugin-code": "^0.16.11",
53+
"validator": "^9.2.0"
5154
},
5255
"keywords": [
5356
"gatsby"
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import React from "react"
2+
import { rhythm } from "../utils/typography"
3+
import presets from "../utils/presets"
4+
import jsonp from "jsonp"
5+
import { validate } from "email-validator"
6+
import { css } from "glamor"
7+
8+
let stripeAnimation = css.keyframes({
9+
"0%": { backgroundPosition: `0 0` },
10+
"100%": { backgroundPosition: `30px 60px` },
11+
})
12+
13+
// Mailchimp endpoint
14+
// From: https://us17.admin.mailchimp.com/lists/integration/embeddedcode?id=XXXXXX
15+
// Where `XXXXXX` is the MC list ID
16+
// Note: we change `/post` to `/post-json`
17+
const MAILCHIMP_URL = `https://gatsbyjs.us17.list-manage.com/subscribe/post-json?u=1dc33f19eb115f7ebe4afe5ee&amp;id=f366064ba7`
18+
19+
class EmailCaptureForm extends React.Component {
20+
constructor() {
21+
super()
22+
this.state = {
23+
email: ``,
24+
}
25+
}
26+
27+
// Update state each time user edits their email address
28+
_handleEmailChange = e => {
29+
this.setState({ email: e.target.value })
30+
}
31+
32+
// Using jsonp, post to MC server & handle its response
33+
_postEmailToMailchimp = url => {
34+
// jsonp lib takes an `endpoint`, {options}, & callback
35+
jsonp(url, { param: `c` }, (err, data) => {
36+
// network failures, timeouts, etc
37+
if (err) {
38+
this.setState({
39+
status: `error`,
40+
msg: err,
41+
})
42+
43+
// Mailchimp errors & failures
44+
} else if (data.result !== `success`) {
45+
this.setState({
46+
status: `error`,
47+
msg: data.msg,
48+
})
49+
50+
// Posted email successfully to Mailchimp
51+
} else {
52+
this.setState({
53+
status: `success`,
54+
msg: data.msg,
55+
})
56+
}
57+
})
58+
}
59+
60+
// On form submit, validate email
61+
// then jsonp to Mailchimp, and update state
62+
_handleFormSubmit = e => {
63+
e.preventDefault()
64+
e.stopPropagation()
65+
66+
// If email is not valid, break early
67+
if (!validate(this.state.email)) {
68+
this.setState({
69+
status: `error`,
70+
msg: `"${this.state.email}" is not a valid email address`,
71+
})
72+
return
73+
}
74+
75+
// Construct the url for our jsonp request
76+
// Query params must be in CAPS
77+
// Capture pathname for better email targeting
78+
const url = `${MAILCHIMP_URL}
79+
&EMAIL=${encodeURIComponent(this.state.email)}
80+
&PATHNAME=${window.location.pathname}
81+
`
82+
83+
this.setState(
84+
{
85+
msg: null,
86+
status: `sending`,
87+
},
88+
// jsonp request as setState callback
89+
this._postEmailToMailchimp(url)
90+
)
91+
}
92+
93+
render() {
94+
return (
95+
<div
96+
css={{
97+
borderTop: `2px solid ${presets.brandLight}`,
98+
marginTop: rhythm(3),
99+
paddingTop: `${rhythm(1)}`,
100+
}}
101+
>
102+
{this.state.status === `success` ? (
103+
<div>Thank you! Youʼll receive your first email shortly.</div>
104+
) : (
105+
<div>
106+
<p>Enjoyed this post? Receive the next one in your inbox!</p>
107+
<form
108+
id="email-capture"
109+
method="post"
110+
noValidate
111+
css={{ margin: 0 }}
112+
>
113+
<div>
114+
<input
115+
type="email"
116+
name="email"
117+
placeholder="[email protected]"
118+
onChange={this._handleEmailChange}
119+
css={{
120+
padding: rhythm(1 / 2),
121+
width: `250px`,
122+
color: presets.bodyColor,
123+
}}
124+
/>
125+
<button
126+
type="submit"
127+
onClick={this._handleFormSubmit}
128+
css={{
129+
borderRadius: `2px`,
130+
border: `1px solid ${presets.brand}`,
131+
cursor: `pointer`,
132+
padding: rhythm(1 / 2),
133+
margin: `${rhythm(1 / 2)} 0 0 ${rhythm(1 / 2)}`,
134+
":hover": {
135+
backgroundSize: `30px 30px`,
136+
backgroundColor: presets.brand,
137+
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)`,
138+
color: `#fff`,
139+
animation: `${stripeAnimation} 2.8s linear infinite`,
140+
},
141+
}}
142+
>
143+
Subscribe
144+
</button>
145+
{this.state.status === `error` && (
146+
<div
147+
dangerouslySetInnerHTML={{ __html: this.state.msg }}
148+
css={{ marginTop: `${rhythm(1 / 2)}` }}
149+
/>
150+
)}
151+
</div>
152+
</form>
153+
</div>
154+
)}
155+
</div>
156+
)
157+
}
158+
}
159+
160+
export default EmailCaptureForm

www/src/templates/template-blog-post.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Img from "gatsby-image"
88
import presets from "../utils/presets"
99
import typography, { rhythm, scale, options } from "../utils/typography"
1010
import Container from "../components/container"
11+
import EmailCaptureForm from "../components/email-capture-form"
1112

1213
class BlogPostTemplate extends React.Component {
1314
render() {
@@ -200,6 +201,7 @@ class BlogPostTemplate extends React.Component {
200201
__html: this.props.data.markdownRemark.html,
201202
}}
202203
/>
204+
<EmailCaptureForm />
203205
</Container>
204206
<div
205207
css={{

0 commit comments

Comments
 (0)