Skip to content

Commit 2c3a297

Browse files
authored
Merge pull request #132 from uidotdev/bytes-email
Bytes email
2 parents bee8cbc + ff35e77 commit 2c3a297

File tree

8 files changed

+249
-24534
lines changed

8 files changed

+249
-24534
lines changed

gatsby-config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
},
2222
"gatsby-plugin-styled-components",
2323
"gatsby-plugin-react-helmet",
24+
"custom-bytes-subscriber-count-plugin",
2425
{
2526
resolve: "gatsby-source-filesystem",
2627
options: {

package-lock.json

Lines changed: 134 additions & 24512 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"react": "^16.13.1",
2121
"react-dom": "^16.13.1",
2222
"react-helmet": "^6.1.0",
23+
"react-hot-toast": "^2.2.0",
2324
"react-syntax-highlighter": "^13.0.0",
2425
"serve": "^11.3.2",
2526
"styled-components": "^5.1.1",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const fetch = require("node-fetch");
2+
3+
exports.sourceNodes = async ({
4+
actions,
5+
createNodeId,
6+
createContentDigest,
7+
}) => {
8+
try {
9+
const res = await fetch(`https://bytes.dev/api/subcount`).then((res) =>
10+
res.json()
11+
);
12+
13+
if (res.error) {
14+
throw res.error;
15+
}
16+
17+
actions.createNode({
18+
subcount: res.subcount,
19+
id: createNodeId(`bytes-subcount`),
20+
internal: {
21+
type: "bytes",
22+
contentDigest: createContentDigest({ subcount: res.subcount }),
23+
},
24+
});
25+
} catch (e) {
26+
console.log(`Error in custom-bytes-subscriber-count-plugin`, e.message);
27+
}
28+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

src/components/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import Helmet from "react-helmet";
3-
import { StaticQuery, graphql, withPrefix } from "gatsby";
3+
import { StaticQuery, graphql } from "gatsby";
44
import ogImage from "./../../static/img/og-image-2.png";
55
import "./global.css";
66

src/components/EmailSignup.js

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,59 @@
11
import React, { useState } from "react";
2+
import { useStaticQuery } from "gatsby";
23
import fetch from "unfetch";
34
import styled from "styled-components";
5+
import toast from "react-hot-toast";
46
import analytics from "./../utils/analytics.js";
57

8+
function sendBytesOptIn({ email, source }) {
9+
return fetch(`https://bytes.dev/api/bytes-optin-cors`, {
10+
method: "POST",
11+
body: JSON.stringify({ email, source }),
12+
headers: {
13+
Accept: "application/json",
14+
"Content-Type": "application/json",
15+
},
16+
}).then((res) => res.json());
17+
}
18+
19+
function useBytesCount() {
20+
const {
21+
bytes: { subcount },
22+
} = useStaticQuery(graphql`
23+
query subCountQuery {
24+
bytes {
25+
subcount
26+
}
27+
}
28+
`);
29+
30+
return subcount;
31+
}
32+
633
const EmailSignup = () => {
34+
const subcount = useBytesCount();
35+
const [isLoading, setIsLoading] = useState(false);
736
const [subscribed, setSubscribed] = useState(false);
837
const [email, setEmail] = useState("");
938

1039
const subscribe = (event) => {
1140
event.preventDefault();
1241
if (!email) return;
13-
analytics.track("subscribe");
14-
setSubscribed(true);
15-
return fetch("https://usehooks-next.vercel.app/api/subscribe", {
16-
method: "POST",
17-
headers: {
18-
"Content-Type": "application/json",
19-
},
20-
body: JSON.stringify({ email }),
21-
}).then((r) => r.json());
42+
setIsLoading(true);
43+
return sendBytesOptIn({ email, source: "useHooks" }).then((res) => {
44+
if (res.error) {
45+
setEmail("");
46+
setIsLoading(false);
47+
return toast.error(
48+
"There was an error. Check to see if your email is correct."
49+
);
50+
}
51+
analytics.track("subscribe");
52+
setSubscribed(true);
53+
setIsLoading(false);
54+
setEmail("");
55+
return toast.success(`Check your email to confirm your subscription.`);
56+
});
2257
};
2358

2459
return (
@@ -33,12 +68,28 @@ const EmailSignup = () => {
3368
</div>
3469
) : (
3570
<>
36-
<Title>
71+
<h4 className="subtitle is-5">
3772
<span role="img" aria-label="letter">
3873
📩
3974
</span>
40-
&nbsp;&nbsp;Get new recipes in your inbox
41-
</Title>
75+
&nbsp;&nbsp;Subscribe to Bytes
76+
</h4>
77+
<p>
78+
Most newsletters are terrible. Thats why we created Bytes. Our
79+
goal was to create a JavaScript newsletter that was both
80+
educational and entertaining. <b>{subcount.toLocaleString()}</b>{" "}
81+
subscribers and an almost 50% weekly open rate later, it looks
82+
like{" "}
83+
<a
84+
target="_blank"
85+
href="https://twitter.com/uidotdev/timelines/1428028877129936899"
86+
>
87+
we did it
88+
</a>{" "}
89+
...
90+
</p>
91+
<br />
92+
4293
<form onSubmit={subscribe}>
4394
<div className="field has-addons">
4495
<div className="control is-expanded">
@@ -53,26 +104,28 @@ const EmailSignup = () => {
53104
</div>
54105
<div className="control">
55106
<button
107+
disabled={isLoading}
56108
className="button is-primary has-text-weight-semibold"
57109
type="submit"
58110
>
59-
Subscribe
111+
{isLoading ? "Loading..." : "Subscribe"}
60112
</button>
61113
</div>
62114
</div>
63115
</form>
64-
<Extra>Join 7,031 subscribers. No spam ever.</Extra>
116+
<Extra>
117+
Join {subcount.toLocaleString()} subscribers. {""}
118+
<a href="https://bytes.dev/archives" target="_blank">
119+
See the most recent issue.
120+
</a>
121+
</Extra>
65122
</>
66123
)}
67124
</div>
68125
</div>
69126
);
70127
};
71128

72-
const Title = styled("div").attrs({ className: "subtitle is-5" })`
73-
margin-bottom: 1.2rem;
74-
`;
75-
76129
const Extra = styled("div")`
77130
margin-top: 1rem;
78131
font-size: 0.8rem;

src/components/Layout.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import styled from "styled-components";
44
import App from "./App";
55
import EmailSignup from "./EmailSignup";
66
import analytics from "./../utils/analytics.js";
7+
import { Toaster } from "react-hot-toast";
78

89
export const Layout = ({ children }) => {
910
return (
@@ -46,10 +47,10 @@ export const Layout = ({ children }) => {
4647
<Container>
4748
<div className="columns is-vcentered ">
4849
<div className="column is-6">
49-
<div className="title is-5">What's all this about?</div>
50+
<h4 className="title is-5">What's all this about?</h4>
5051
<p>
51-
<i>Hooks</i> are a new addition in React that lets you use state
52-
and other React features without writing a class. This website
52+
<i>Hooks</i> are a feature in React that allow you use state and
53+
other React features without writing classes. This website
5354
provides easy to understand code examples to help you learn how
5455
hooks work and inspire you to take advantage of them in your
5556
next project.
@@ -129,6 +130,14 @@ export const Layout = ({ children }) => {
129130
</FooterLevel>
130131
</div>
131132
</div>
133+
<Toaster
134+
position="bottom-right"
135+
toastOptions={{
136+
success: {
137+
duration: 3000,
138+
},
139+
}}
140+
/>
132141
</App>
133142
);
134143
};

0 commit comments

Comments
 (0)