-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Adds email capture to bottom of blog #3333
Changes from 5 commits
253e3be
19c6d10
dd77175
b1c4eaf
78f3c4b
a860856
3066ff5
0d6f9f9
9d1df14
00b2fd8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import React from "react" | ||
import { rhythm } from "../utils/typography" | ||
import presets from "../utils/presets" | ||
import jsonp from "jsonp" | ||
|
||
// 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 { | ||
state = { | ||
email: ``, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be set in the constructor eg https://github.com/gatsbyjs/gatsby/blob/master/www/src/components/markdown-page-footer.js#L27-L30 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although React docs do say to initialize state within the constructor, I've been reading a lot about how that's not necessary if you don't have props, like here. https://stackoverflow.com/a/37788410 and this post touches on this concept as well: https://babeljs.io/blog/2015/06/07/react-on-es6-plus But happy to refactor using the constructor. Just thought I'd share anyway. |
||
|
||
// Update state each time user edits their email address | ||
_handleEmailChange = e => { | ||
this.setState({ email: e.target.value }) | ||
} | ||
|
||
// Check whether the email address is valid: | ||
// not an empty string, | ||
// greater than 5 characters, | ||
// includes both `@` and `.` | ||
_isValidEmailAddress = email => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's better to off-load common algorithms like this to an NPM module e.g. https://github.com/Sembiance/email-validator |
||
!!email && email.length > 5 && (email.includes(`@`) && email.includes(`.`)) | ||
|
||
_handleFormSubmit = e => { | ||
e.preventDefault() | ||
e.stopPropagation() | ||
|
||
// If email is not valid, break early | ||
if (!this._isValidEmailAddress(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 | ||
const url = `${MAILCHIMP_URL} | ||
&EMAIL=${encodeURIComponent(this.state.email)} | ||
&PATHNAME=${window.location.pathname} | ||
` | ||
|
||
this.setState( | ||
{ | ||
msg: null, | ||
status: `sending`, | ||
}, | ||
// setState callback (jsonp) | ||
() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we extract & name this function, eg const postEmailToMailchimp? |
||
jsonp( | ||
url, | ||
{ | ||
param: `c`, | ||
}, | ||
// jsonp callback | ||
(err, data) => { | ||
if (err) { | ||
this.setState({ | ||
status: `error`, | ||
msg: err, | ||
}) | ||
} else if (data.result !== `success`) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does this state represent? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (just curious, might be useful to comment) |
||
this.setState({ | ||
status: `error`, | ||
msg: data.msg, | ||
}) | ||
} else { | ||
this.setState({ | ||
status: `success`, | ||
msg: data.msg, | ||
}) | ||
} | ||
} | ||
) | ||
) | ||
} | ||
|
||
render() { | ||
return ( | ||
<div | ||
css={{ | ||
border: `2px solid ${presets.brand}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the "rhythm" function for spacings |
||
backgroundColor: presets.veryLightPurple, | ||
borderRadius: `4px`, | ||
padding: `${rhythm(0.75)}`, | ||
}} | ||
> | ||
{this.state.status === `success` ? ( | ||
<div>Thank you! Youʼll receive your first email shortly.</div> | ||
) : ( | ||
<div> | ||
Enjoyed this post? Receive the next one in your inbox! | ||
<br /> | ||
<form id="email-capture" method="post" noValidate css={{margin: 0}}> | ||
<div> | ||
<input | ||
type="email" | ||
name="email" | ||
placeholder="you@email.com" | ||
onChange={this._handleEmailChange} | ||
css={{ | ||
marginTop: rhythm(0.3), | ||
padding: `${rhythm(0.3)} ${rhythm(0.3)} ${rhythm( | ||
0.3 | ||
)} ${rhythm(0.7)}`, | ||
width: `250px`, | ||
}} | ||
/> | ||
<button | ||
type="submit" | ||
onClick={this._handleFormSubmit} | ||
css={{ | ||
borderRadius: `2px`, | ||
border: `2px solid ${presets.brand}`, | ||
backgroundColor: presets.brand, | ||
color: `black`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
height: `43px`, | ||
padding: `0 ${rhythm(0.75)} 0 ${rhythm(0.75)}`, | ||
margin: `${rhythm(0.75)} 0 0 ${rhythm(0.75)}`, | ||
}} | ||
> | ||
Subscribe | ||
</button> | ||
{this.state.status === `error` && ( | ||
<div | ||
dangerouslySetInnerHTML={{ __html: this.state.msg }} | ||
css={{ marginTop: `${rhythm(0.5)}` }} | ||
/> | ||
)} | ||
</div> | ||
</form> | ||
</div> | ||
)} | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export default EmailCaptureForm |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.