From f54b86d0b7e7d35ce682b7b18f69d679c8a560ce Mon Sep 17 00:00:00 2001 From: Derek Sonnenberg Date: Wed, 6 Dec 2023 23:23:45 -0600 Subject: [PATCH] refactor: clean the code PE-5161 --- .github/workflows/deploy.yml | 2 +- src/GiftForm.css | 4 ++ src/GiftForm.tsx | 108 +++++++++------------------------ src/constants.ts | 4 ++ src/hooks/useCreditsForFiat.ts | 32 ++++++++++ src/hooks/useWincForOneGiB.ts | 20 ++++++ src/utils/getTopUpQuote.ts | 18 ++++++ 7 files changed, 107 insertions(+), 81 deletions(-) create mode 100644 src/hooks/useCreditsForFiat.ts create mode 100644 src/hooks/useWincForOneGiB.ts create mode 100644 src/utils/getTopUpQuote.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2221393..17d50bd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,7 @@ name: Deploy to GitHub Pages on: push: branches: - - PE-5161_gift_app_init + - prod jobs: deploy: diff --git a/src/GiftForm.css b/src/GiftForm.css index daad379..685a7f5 100644 --- a/src/GiftForm.css +++ b/src/GiftForm.css @@ -43,6 +43,10 @@ background-color: var(--dark-gray); } +/* + If the input is not focused, and the placeholder is not shown, and the input is invalid, + then change the border color to red to indicate invalid email address. +*/ #recipient-email:not(:focus):not(:placeholder-shown):invalid { border-color: var(--ardrive-red); } diff --git a/src/GiftForm.tsx b/src/GiftForm.tsx index b2c022f..a629cc8 100644 --- a/src/GiftForm.tsx +++ b/src/GiftForm.tsx @@ -1,104 +1,55 @@ -import { TurboFactory, USD, TopUpRawResponse } from "@ardrive/turbo-sdk"; -import { useState, useEffect, useRef } from "react"; +import { useState, useRef } from "react"; import { defaultUSDAmount, - paymentServiceUrl, termsOfServiceUrl, + wincPerCredit, } from "./constants"; import useDebounce from "./hooks/useDebounce"; import "./GiftForm.css"; +import { getTopUpQuote } from "./utils/getTopUpQuote"; +import { useWincForOneGiB } from "./hooks/useWincForOneGiB"; +import { useCreditsForFiat } from "./hooks/useCreditsForFiat"; interface GiftFormProps { errorCallback: (message: string) => void; } -async function getTopUpQuote( - usdAmount: number, - recipientEmail: string, -): Promise { - // TODO: support emails on turbo sdk - // turbo.createCheckoutSession({amount: USD(usdAmount / 100), email: recipientEmail,owner}}) - const response = await fetch( - `${paymentServiceUrl}/v1/top-up/checkout-session/${recipientEmail}/usd/${ - usdAmount * 100 - }?destinationAddressType=email`, - ); - const data = await response.json(); - console.log("data", data); - - return data; -} +const maxUSDAmount = 10000; +const minUSDAmount = 5; export function GiftForm({ errorCallback }: GiftFormProps) { - const [recipientEmail, setRecipientEmail] = useState(""); - const [termsAccepted, setTermsAccepted] = useState(false); - - const recipientEmailRef = useRef(null); - const [usdAmount, setUsdAmount] = useState(defaultUSDAmount); - const debouncedUsdAmount = useDebounce(usdAmount, 500); + const [recipientEmail, setRecipientEmail] = useState(""); + const [isTermsAccepted, setTermsAccepted] = useState(false); const handleUSDChange = (e: React.ChangeEvent) => { const amount = Number(e.target.value); - if (amount > 10000) { - setUsdAmount(10000); + if (amount > maxUSDAmount) { + setUsdAmount(maxUSDAmount); return; } - if (amount < 5) { - setUsdAmount(5); + if (amount < minUSDAmount) { + setUsdAmount(minUSDAmount); return; } setUsdAmount(Number(Number(e.target.value).toFixed(2))); }; - const [credits, setCredits] = useState(undefined); - - const usdWhenCreditsWereLastUpdatedRef = useRef( - undefined, + const wincForOneGiB = useWincForOneGiB(); + const debouncedUsdAmount = useDebounce(usdAmount, 500); + const [credits, usdWhenCreditsWereLastUpdatedRef] = useCreditsForFiat( + debouncedUsdAmount, + errorCallback, ); - const [wincForOneGiB, setWincForOneGiB] = useState( - undefined, - ); - useEffect(() => { - const turbo = TurboFactory.unauthenticated({ - paymentServiceConfig: { url: paymentServiceUrl }, - }); - turbo.getFiatRates().then(({ winc }) => { - setWincForOneGiB(winc); - }); - }, []); - - // Get credits for USD amount when USD amount changes - useEffect(() => { - const getCreditsAndGiBForUSD = async ( - usdAmount: number, - ): Promise => { - const turbo = TurboFactory.unauthenticated({ - paymentServiceConfig: { url: paymentServiceUrl }, - }); - const { winc } = await turbo.getWincForFiat({ - amount: USD(usdAmount), - promoCodes: [], - }); // todo: add promo codes support - return winc; - }; - getCreditsAndGiBForUSD(debouncedUsdAmount) - .then((credits) => { - setCredits(credits); - }) - .catch((err) => { - console.error(err); - errorCallback(`Error getting credits for USD amount: ${err.message}`); - }); - usdWhenCreditsWereLastUpdatedRef.current = debouncedUsdAmount; - }, [debouncedUsdAmount, errorCallback]); + const recipientEmailRef = useRef(null); + const isEmailHtmlElementValid = recipientEmailRef.current?.checkValidity(); const canSubmitForm = !!credits && !!recipientEmail && - !!termsAccepted && - recipientEmailRef.current?.checkValidity(); + !!isTermsAccepted && + isEmailHtmlElementValid; const handleSubmit = (e: React.MouseEvent) => { e.preventDefault(); @@ -108,9 +59,7 @@ export function GiftForm({ errorCallback }: GiftFormProps) { } getTopUpQuote(usdAmount, recipientEmail).then((topUpQuote) => { - const url = topUpQuote.paymentSession.url; - - window.location.href = url; + window.location.href = topUpQuote.paymentSession.url; }); }; @@ -171,15 +120,14 @@ export function GiftForm({ errorCallback }: GiftFormProps) {
{"$".toLocaleUpperCase()} - {usdWhenCreditsWereLastUpdatedRef.current} + {usdWhenCreditsWereLastUpdatedRef} {" "} - ≈{" "} - - {(Number(credits) / 1_000_000_000_000).toFixed(4)} - + ≈ {credits.toFixed(4)} Credits ≈{" "} - {(Number(credits) / Number(wincForOneGiB)).toFixed(2)} + {( + Number(credits * wincPerCredit) / Number(wincForOneGiB) + ).toFixed(2)} GiB
diff --git a/src/constants.ts b/src/constants.ts index 16ae337..2fa4094 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,3 +4,7 @@ export const paymentServiceUrl = : "http://localhost:3000"; export const termsOfServiceUrl = "https://ardrive.io/tos-and-privacy/"; export const defaultUSDAmount = 10.0; +export const turboConfig = { + paymentServiceConfig: { url: paymentServiceUrl }, +}; +export const wincPerCredit = 1_000_000_000_000; diff --git a/src/hooks/useCreditsForFiat.ts b/src/hooks/useCreditsForFiat.ts new file mode 100644 index 0000000..6d6e723 --- /dev/null +++ b/src/hooks/useCreditsForFiat.ts @@ -0,0 +1,32 @@ +import { TurboFactory, USD } from "@ardrive/turbo-sdk"; +import { useState, useRef, useEffect } from "react"; +import { turboConfig, wincPerCredit } from "../constants"; + +export function useCreditsForFiat( + debouncedUsdAmount: number, + errorCallback: (message: string) => void, +): [number | undefined, number | undefined] { + const [winc, setWinc] = useState(undefined); + const usdWhenCreditsWereLastUpdatedRef = useRef( + undefined, + ); + + // Get credits for USD amount when USD amount has stopped debouncing + useEffect(() => { + TurboFactory.unauthenticated(turboConfig) + .getWincForFiat({ amount: USD(debouncedUsdAmount), promoCodes: [] }) + .then(({ winc }) => { + usdWhenCreditsWereLastUpdatedRef.current = debouncedUsdAmount; + setWinc(winc); + }) + .catch((err) => { + console.error(err); + errorCallback(`Error getting credits for USD amount: ${err.message}`); + }); + }, [debouncedUsdAmount, errorCallback]); + + return [ + winc ? +winc / wincPerCredit : undefined, + usdWhenCreditsWereLastUpdatedRef.current, + ]; +} diff --git a/src/hooks/useWincForOneGiB.ts b/src/hooks/useWincForOneGiB.ts new file mode 100644 index 0000000..3e0586e --- /dev/null +++ b/src/hooks/useWincForOneGiB.ts @@ -0,0 +1,20 @@ +import { TurboFactory } from "@ardrive/turbo-sdk"; +import { useState, useEffect } from "react"; +import { turboConfig } from "../constants"; + +export function useWincForOneGiB() { + const [wincForOneGiB, setWincForOneGiB] = useState( + undefined, + ); + + // On first render, get winc for 1 GiB for conversions + useEffect(() => { + TurboFactory.unauthenticated(turboConfig) + .getFiatRates() + .then(({ winc }) => { + setWincForOneGiB(winc); + }); + }, []); + + return wincForOneGiB; +} diff --git a/src/utils/getTopUpQuote.ts b/src/utils/getTopUpQuote.ts new file mode 100644 index 0000000..34ec3e0 --- /dev/null +++ b/src/utils/getTopUpQuote.ts @@ -0,0 +1,18 @@ +import { TopUpRawResponse } from "@ardrive/turbo-sdk"; +import { paymentServiceUrl } from "../constants"; + +export async function getTopUpQuote( + usdAmount: number, + recipientEmail: string, +): Promise { + // TODO: support emails on turbo sdk + // return turboFactory.unauthenticated(turboConfig).createCheckoutSession({ amount: USD(usdAmount / 100), email: recipientEmail }) + const response = await fetch( + `${paymentServiceUrl}/v1/top-up/checkout-session/${recipientEmail}/usd/${ + usdAmount * 100 + }?destinationAddressType=email`, + ); + const data = await response.json(); + + return data; +}