Moved donate modal to its own route

Changed routes to be kebab case instead of camel case
This commit is contained in:
Mitchell McCaffrey 2020-10-16 22:09:36 +11:00
parent becb2bfaad
commit 5b9cabf532
9 changed files with 210 additions and 150 deletions

1
.env
View File

@ -1,3 +1,4 @@
REACT_APP_BROKER_URL=http://localhost:9000
REACT_APP_ICE_SERVERS_URL=http://localhost:9000/iceservers
REACT_APP_STRIPE_API_KEY=pk_test_8M3NHrF1eI2b84ubF4F8rSTe0095R3f0My
REACT_APP_VERSION=$npm_package_version

View File

@ -1,3 +1,4 @@
REACT_APP_BROKER_URL=https://test.owlbear.rodeo
REACT_APP_ICE_SERVERS_URL=https://test.owlbear.rodeo/iceservers
REACT_APP_STRIPE_API_KEY=pk_live_MJjzi5djj524Y7h3fL5PNh4e00a852XD51
REACT_APP_VERSION=$npm_package_version

View File

@ -9,6 +9,7 @@ import About from "./routes/About";
import FAQ from "./routes/FAQ";
import ReleaseNotes from "./routes/ReleaseNotes";
import HowTo from "./routes/HowTo";
import Donate from "./routes/Donate";
import { AuthProvider } from "./contexts/AuthContext";
import { DatabaseProvider } from "./contexts/DatabaseContext";
@ -27,10 +28,14 @@ function App() {
<KeyboardProvider>
<Router>
<Switch>
<Route path="/howTo">
<Route path="/donate">
<Donate />
</Route>
{/* Legacy support camel case routes */}
<Route path={["/howTo", "/how-to"]}>
<HowTo />
</Route>
<Route path="/releaseNotes">
<Route path={["/releaseNotes", "/release-notes"]}>
<ReleaseNotes />
</Route>
<Route path="/about">

View File

@ -24,10 +24,10 @@ function Footer() {
<Link m={2} to="/faq" variant="footer">
FAQ
</Link>
<Link m={2} to="/releaseNotes" variant="footer">
<Link m={2} to="/release-notes" variant="footer">
Release Notes
</Link>
<Link m={2} to="/howTo" variant="footer">
<Link m={2} to="/how-to" variant="footer">
How To
</Link>
</Flex>

19
src/icons/DonateIcon.js Normal file
View File

@ -0,0 +1,19 @@
import React from "react";
function DonateIcon() {
return (
<svg
width="16"
height="16"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentcolor"
style={{ margin: "0 4px" }}
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M13.35 20.13c-.76.69-1.93.69-2.69-.01l-.11-.1C5.3 15.27 1.87 12.16 2 8.28c.06-1.7.93-3.33 2.34-4.29 2.64-1.8 5.9-.96 7.66 1.1 1.76-2.06 5.02-2.91 7.66-1.1 1.41.96 2.28 2.59 2.34 4.29.14 3.88-3.3 6.99-8.55 11.76l-.1.09z" />
</svg>
);
}
export default DonateIcon;

View File

@ -1,134 +0,0 @@
import React, { useState, useEffect } from "react";
import { Box, Label, Button, Flex, Radio, Text } from "theme-ui";
import { useLocation, useHistory } from "react-router-dom";
import Modal from "../components/Modal";
import LoadingOverlay from "../components/LoadingOverlay";
import Banner from "../components/Banner";
const skus = [
{ sku: "sku_H6DhHS1MimRPR9", price: "$5.00 AUD", name: "Small" },
{ sku: "sku_H6DhiQfHUkYUKd", price: "$15.00 AUD", name: "Medium" },
{ sku: "sku_H6DhbO2oUn9Sda", price: "$30.00 AUD", name: "Large" },
];
function DonationModal({ isOpen, onRequestClose }) {
// Handle callback from stripe
const location = useLocation();
const history = useHistory();
const query = new URLSearchParams(location.search);
const hasDonated = query.has("donated");
const showDonationForm = isOpen || query.get("donated") === "false";
const [loading, setLoading] = useState(showDonationForm);
const [error, setError] = useState(null);
const [stripe, setStripe] = useState();
useEffect(() => {
if (showDonationForm) {
import("@stripe/stripe-js").then(({ loadStripe }) => {
loadStripe("pk_live_MJjzi5djj524Y7h3fL5PNh4e00a852XD51")
.then((stripe) => {
setStripe(stripe);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
});
}
}, [showDonationForm]);
function handleClose() {
if (hasDonated) {
history.push(location.pathname);
}
onRequestClose();
}
function handleSubmit(event) {
event.preventDefault();
if (!stripe) {
return;
}
setLoading(true);
stripe
.redirectToCheckout({
items: [{ sku: selectedSku, quantity: 1 }],
successUrl: `${window.location.href}?donated=true`,
cancelUrl: `${window.location.href}?donated=false`,
submitType: "donate",
})
.then((response) => {
setLoading(false);
if (response.error) {
setError(response.error.message);
}
});
}
const [selectedSku, setSelectedSku] = useState("sku_H6DhiQfHUkYUKd");
function handleSkuChange(event) {
setSelectedSku(event.target.value);
}
const donationSuccessful = (
<Box>
<Text my={2} variant="heading" as="h1" sx={{ fontSize: 3 }}>
Thanks for donating! ʕʔ
</Text>
</Box>
);
const donationForm = (
<Box as="form" onSubmit={handleSubmit}>
<Label py={2}>Support us with a donation</Label>
<Text as="p" mb={2} variant="caption">
One time donation
</Text>
{skus.map((sku) => (
<Label key={sku.sku}>
<Radio
name="donation"
checked={selectedSku === sku.sku}
value={sku.sku}
onChange={handleSkuChange}
/>
{sku.name} - {sku.price}
</Label>
))}
<Flex mt={3}>
<Button sx={{ flexGrow: 1 }} disabled={!stripe || loading}>
Donate
</Button>
</Flex>
</Box>
);
return (
<Modal isOpen={isOpen || hasDonated} onRequestClose={handleClose}>
<Flex
sx={{
flexDirection: "column",
justifyContent: "center",
maxWidth: "300px",
flexGrow: 1,
}}
m={2}
>
{query.get("donated") === "true" ? donationSuccessful : donationForm}
{loading && <LoadingOverlay />}
<Banner isOpen={!!error} onRequestClose={() => setError(null)}>
<Box p={1}>
<Text as="p" variant="body2">
{error}
</Text>
</Box>
</Banner>
</Flex>
</Modal>
);
}
export default DonationModal;

View File

@ -22,7 +22,7 @@ function About() {
m={4}
>
<Text my={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
About ʕʔ
About <span aria-hidden="true">ʕʔ</span>
</Text>
<Text my={1} mt={2} variant="heading" as="h3" sx={{ fontSize: 3 }}>
The Goal

160
src/routes/Donate.js Normal file
View File

@ -0,0 +1,160 @@
import React, { useEffect, useState } from "react";
import {
Box,
Flex,
Text,
Message,
Button,
Input,
Label,
Radio,
} from "theme-ui";
import { useLocation } from "react-router-dom";
import Footer from "../components/Footer";
import Banner from "../components/Banner";
import LoadingOverlay from "../components/LoadingOverlay";
const prices = [
{ price: "$5.00", name: "Small", value: 5 },
{ price: "$15.00", name: "Medium", value: 15 },
{ price: "$30.00", name: "Large", value: 30 },
];
function Donate() {
const location = useLocation();
const query = new URLSearchParams(location.search);
const hasDonated = query.has("success");
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [stripe, setStripe] = useState();
useEffect(() => {
import("@stripe/stripe-js").then(({ loadStripe }) => {
loadStripe(process.env.REACT_APP_STRIPE_API_KEY)
.then((stripe) => {
setStripe(stripe);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
});
}, []);
async function handleSubmit(event) {
event.preventDefault();
if (loading) {
return;
}
const response = await fetch("/create-session", { method: "POST" });
const session = await response.json();
const result = await stripe.redirectToCheckout({ sessionId: session.id });
if (result.error) {
setError(result.error.message);
}
}
const [selectedPrice, setSelectedPrice] = useState("Medium");
const [value, setValue] = useState(15);
function handlePriceChange(price) {
setValue(price.value);
setSelectedPrice(price.name);
}
return (
<Flex
sx={{
flexDirection: "column",
justifyContent: "space-between",
minHeight: "100%",
alignItems: "center",
}}
>
<Flex
sx={{
flexDirection: "column",
maxWidth: "350px",
width: "100%",
flexGrow: 1,
}}
m={4}
as="form"
onSubmit={handleSubmit}
>
<Text my={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
Donate
</Text>
{hasDonated ? (
<Message my={2}>Thanks for donating!</Message>
) : (
<Text variant="body2" as="p">
In order to keep Owlbear Rodeo running any donation is greatly
appreciated.
</Text>
)}
<Text
my={4}
variant="heading"
as="h1"
sx={{ fontSize: 5, alignSelf: "center" }}
aria-hidden="true"
>
()*:
</Text>
<Text as="p" mb={2} variant="caption">
One time donation (USD)
</Text>
<Box sx={{ display: "flex", flexWrap: "wrap" }}>
{prices.map((price) => (
<Label mx={1} key={price.name} sx={{ width: "initial" }}>
<Radio
name="donation"
checked={selectedPrice === price.name}
onChange={() => handlePriceChange(price)}
/>
{price.price}
</Label>
))}
<Label mx={1} sx={{ width: "initial" }}>
<Radio
name="donation"
checked={selectedPrice === "Custom"}
onChange={() => handlePriceChange({ value, name: "Custom" })}
/>
Custom
</Label>
</Box>
{selectedPrice === "Custom" && (
<Box>
<Label htmlFor="donation">Amount ($)</Label>
<Input
type="number"
name="donation"
min={1}
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</Box>
)}
<Button my={3} disabled={loading || !value}>
Go to Payment
</Button>
</Flex>
<Footer />
{loading && <LoadingOverlay />}
<Banner isOpen={!!error} onRequestClose={() => setError(null)}>
<Box p={1}>
<Text as="p" variant="body2">
{error}
</Text>
</Box>
</Banner>
</Flex>
);
}
export default Donate;

View File

@ -1,24 +1,24 @@
import React, { useState, useEffect, useContext } from "react";
import { Flex, Button, Image, Text, IconButton, Link } from "theme-ui";
import { useHistory } from "react-router-dom";
import Footer from "../components/Footer";
import StartModal from "../modals/StartModal";
import JoinModal from "../modals/JoinModal";
import DonateModal from "../modals/DonationModal";
import AuthContext from "../contexts/AuthContext";
import RedditIcon from "../icons/SocialRedditIcon";
import TwitterIcon from "../icons/SocialTwitterIcon";
import YouTubeIcon from "../icons/SocialYouTubeIcon";
import DonateIcon from "../icons/DonateIcon";
import owlington from "../images/Owlington.png";
function Home() {
const [isStartModalOpen, setIsStartModalOpen] = useState(false);
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
const [isDonateModalOpen, setIsDonateModalOpen] = useState(false);
// Reset password on visiting home
const { setPassword } = useContext(AuthContext);
@ -26,6 +26,8 @@ function Home() {
setPassword("");
}, [setPassword]);
const history = useHistory();
return (
<Flex
sx={{
@ -58,13 +60,23 @@ function Home() {
Beta v{process.env.REACT_APP_VERSION}
</Text>
<Button
m={2}
onClick={() => setIsDonateModalOpen(true)}
variant="secondary"
as="a"
href="/donate"
my={4}
mx={2}
onClick={(e) => {
e.preventDefault();
history.push("/donate");
}}
sx={{
display: "flex",
alignItems: "flex-end",
justifyContent: "center",
}}
>
Support Us
Donate <DonateIcon />
</Button>
<Flex sx={{ justifyContent: "center" }}>
<Flex mb={4} mt={0} sx={{ justifyContent: "center" }}>
<Link href="https://www.reddit.com/r/OwlbearRodeo/">
<IconButton title="Reddit" aria-label="Reddit">
<RedditIcon />
@ -89,10 +101,6 @@ function Home() {
isOpen={isStartModalOpen}
onRequestClose={() => setIsStartModalOpen(false)}
/>
<DonateModal
isOpen={isDonateModalOpen}
onRequestClose={() => setIsDonateModalOpen(false)}
/>
</Flex>
<Footer />
</Flex>