diff --git a/.env b/.env index 00465e2..166934f 100644 --- a/.env +++ b/.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 \ No newline at end of file diff --git a/.env.production b/.env.production index 7d3d130..8967d70 100644 --- a/.env.production +++ b/.env.production @@ -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 \ No newline at end of file diff --git a/src/App.js b/src/App.js index e82e3d2..ccfb682 100644 --- a/src/App.js +++ b/src/App.js @@ -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() { - + + + + {/* Legacy support camel case routes */} + - + diff --git a/src/components/Footer.js b/src/components/Footer.js index dc7b32b..aa83ee9 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -24,10 +24,10 @@ function Footer() { FAQ - + Release Notes - + How To diff --git a/src/icons/DonateIcon.js b/src/icons/DonateIcon.js new file mode 100644 index 0000000..3d4e819 --- /dev/null +++ b/src/icons/DonateIcon.js @@ -0,0 +1,19 @@ +import React from "react"; + +function DonateIcon() { + return ( + + + + + ); +} + +export default DonateIcon; diff --git a/src/modals/DonationModal.js b/src/modals/DonationModal.js deleted file mode 100644 index 287e632..0000000 --- a/src/modals/DonationModal.js +++ /dev/null @@ -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 = ( - - - Thanks for donating! ʕ•ᴥ•ʔ - - - ); - - const donationForm = ( - - - - One time donation - - {skus.map((sku) => ( - - ))} - - - - - ); - - return ( - - - {query.get("donated") === "true" ? donationSuccessful : donationForm} - {loading && } - setError(null)}> - - - {error} - - - - - - ); -} - -export default DonationModal; diff --git a/src/routes/About.js b/src/routes/About.js index 790371a..48cc76a 100644 --- a/src/routes/About.js +++ b/src/routes/About.js @@ -22,7 +22,7 @@ function About() { m={4} > - About ʕ•ᴥ•ʔ + About The Goal diff --git a/src/routes/Donate.js b/src/routes/Donate.js new file mode 100644 index 0000000..35f4268 --- /dev/null +++ b/src/routes/Donate.js @@ -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 ( + + + + Donate + + {hasDonated ? ( + Thanks for donating! + ) : ( + + In order to keep Owlbear Rodeo running any donation is greatly + appreciated. + + )} + + + One time donation (USD) + + + {prices.map((price) => ( + + ))} + + + {selectedPrice === "Custom" && ( + + + setValue(e.target.value)} + /> + + )} + + +