damus.io

damus.io website
git clone git://jb55.com/damus.io
Log | Files | Refs | README | LICENSE

PurpleCheckout.tsx (4944B)


      1 import { useIntl } from "react-intl";
      2 import { useEffect, useState } from "react";
      3 import { useInterval } from 'usehooks-ts'
      4 import { Info } from "lucide-react";
      5 import { PurpleLayout } from "../PurpleLayout";
      6 import { AccountInfo } from "@/utils/PurpleUtils";
      7 import { Step1ProductSelection } from "./PurpleCheckoutDetails/Step1ProductSelection";
      8 import { LNCheckout } from "./PurpleCheckoutDetails/Types";
      9 import { Step2UserVerification } from "./PurpleCheckoutDetails/Step2UserVerification";
     10 import { Step3Payment } from "./PurpleCheckoutDetails/Step3Payment";
     11 import { PurpleCheckoutErrorDialog } from "./PurpleCheckoutDetails/ErrorDialog";
     12 import { CheckoutSuccess } from "./PurpleCheckoutDetails/CheckoutSuccess";
     13 
     14 
     15 export function PurpleCheckout() {
     16   const intl = useIntl()
     17   const [lnCheckout, setLNCheckout] = useState<LNCheckout | null>(null) // The checkout object from the server
     18   const [error, setError] = useState<string | null>(null)  // An error message to display to the user
     19   const [selectedAuthMethod, setSelectedAuthMethod] = useState<string | "nostr-dm" | "damus-ios">("nostr-dm")
     20   const [existingAccountInfo, setExistingAccountInfo] = useState<AccountInfo | null | undefined>(undefined)  // The account info fetched from the server
     21 
     22   // MARK: - Functions
     23 
     24   const refreshLNCheckout = async (id?: string) => {
     25     if (!lnCheckout && !id) {
     26       return
     27     }
     28     try {
     29       const response = await fetch(process.env.NEXT_PUBLIC_PURPLE_API_BASE_URL + "/ln-checkout/" + (id || lnCheckout?.id), {
     30         method: 'GET',
     31         headers: {
     32           'Content-Type': 'application/json'
     33         },
     34       })
     35       const data: LNCheckout = await response.json()
     36       setLNCheckout(data)
     37     }
     38     catch (e) {
     39       console.error(e)
     40       setError("Failed to get checkout info from our servers, please wait a few minutes and try to refresh this page. If the problem persists, please contact support.")
     41     }
     42   }
     43 
     44   const pollState = async () => {
     45     if (!lnCheckout) {
     46       return
     47     }
     48     if (!lnCheckout.verified_pubkey) {
     49       refreshLNCheckout()
     50     }
     51   }
     52 
     53   // MARK: - Effects and hooks
     54 
     55   // Keep checking the state of things when needed
     56   useInterval(pollState, 1000)
     57 
     58   useEffect(() => {
     59     // Set the query parameter on the URL to be the lnCheckout ID to avoid losing it on page refresh
     60     if (lnCheckout) {
     61       const url = new URL(window.location.href)
     62       url.searchParams.set("id", lnCheckout.id)
     63       window.history.replaceState({}, "", url.toString())
     64     }
     65   }, [lnCheckout])
     66 
     67   // Load the LN checkout (if there is one) on page load
     68   useEffect(() => {
     69     // Check if there is a lnCheckout ID in the URL query parameters. If so, fetch the lnCheckout
     70     const url = new URL(window.location.href)
     71     const id = url.searchParams.get("id")
     72     if (id) {
     73       console.log("Found lnCheckout ID in URL query parameters. Fetching lnCheckout...")
     74       refreshLNCheckout(id)
     75     }
     76   }, [])
     77 
     78   // MARK: - Render
     79 
     80   return (<>
     81     <PurpleCheckoutErrorDialog lnCheckout={lnCheckout} error={error} setError={setError} />
     82     
     83     <PurpleLayout>
     84       <h2 className="text-2xl text-left text-purple-200 font-semibold break-keep mb-2">
     85         {intl.formatMessage({ id: "purple.checkout.title", defaultMessage: "Checkout" })}
     86       </h2>
     87       <div className="text-purple-200/70 text-normal text-left mb-6 font-semibold flex flex-col md:flex-row gap-3 rounded-lg bg-purple-200/10 p-3 items-center md:items-start">
     88         <Info className="w-6 h-6 shrink-0 mt-0 md:mt-1" />
     89         <div className="flex flex-col text-center md:text-left">
     90           <span className="text-normal md:text-lg mb-2">
     91             {intl.formatMessage({ id: "purple.checkout.description", defaultMessage: "New accounts and renewals" })}
     92           </span>
     93           <span className="text-xs text-purple-200/50">
     94             {intl.formatMessage({ id: "purple.checkout.description-2", defaultMessage: "Use this page to purchase a new account, or to renew an existing one. You will need the latest Damus version to complete the checkout." })}
     95           </span>
     96         </div>
     97       </div>
     98       
     99       <Step1ProductSelection
    100         lnCheckout={lnCheckout}
    101         setLNCheckout={setLNCheckout}
    102         setError={setError}
    103       />
    104       
    105       <Step2UserVerification
    106         lnCheckout={lnCheckout}
    107         setLNCheckout={setLNCheckout}
    108         selectedAuthMethod={selectedAuthMethod}
    109         setSelectedAuthMethod={setSelectedAuthMethod}
    110         setError={setError}
    111       />
    112       
    113       <Step3Payment
    114         lnCheckout={lnCheckout}
    115         setLNCheckout={setLNCheckout}
    116         setError={setError}
    117         successView={<>
    118           {lnCheckout &&
    119             <CheckoutSuccess
    120               lnCheckout={lnCheckout}
    121               existingAccountInfo={existingAccountInfo}
    122               selectedAuthMethod={selectedAuthMethod}
    123               setError={setError}
    124             />
    125           }
    126         </>}
    127       />
    128     </PurpleLayout>
    129   </>)
    130 }