Moved donate modal to its own route
Changed routes to be kebab case instead of camel case
This commit is contained in:
parent
becb2bfaad
commit
5b9cabf532
1
.env
1
.env
@ -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
|
@ -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
|
@ -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">
|
||||
|
@ -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
19
src/icons/DonateIcon.js
Normal 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;
|
@ -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;
|
@ -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
160
src/routes/Donate.js
Normal 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;
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user