diff --git a/packages/keychain/src/components/app.tsx b/packages/keychain/src/components/app.tsx index 0f580bbef..80a27a623 100644 --- a/packages/keychain/src/components/app.tsx +++ b/packages/keychain/src/components/app.tsx @@ -7,6 +7,7 @@ import { Success } from "./success"; import { Pending } from "./pending"; import { Consent, Slot } from "./slot"; import { OcclusionDetector } from "./OcclusionDetector"; +import { Fund } from "./slot/fund"; export function App() { return ( @@ -19,6 +20,7 @@ export function App() { } /> }> } /> + } /> } /> } /> diff --git a/packages/keychain/src/components/funding/Balance.tsx b/packages/keychain/src/components/funding/Balance.tsx index 341ee94fb..491b87551 100644 --- a/packages/keychain/src/components/funding/Balance.tsx +++ b/packages/keychain/src/components/funding/Balance.tsx @@ -10,26 +10,34 @@ import { TokenPair } from "@cartridge/utils/api/cartridge"; import { formatEther } from "viem"; import { ETH_CONTRACT_ADDRESS, + isIframe, useCountervalue, useCreditBalance, useERC20Balance, } from "@cartridge/utils"; import { useController } from "@/hooks/controller"; +import { useEffect, useState } from "react"; +import Controller from "@/utils/controller"; + +export type BalanceType = "credits" | "eth" | "strk"; type BalanceProps = { - showBalances: ("credits" | "eth" | "strk")[]; + showBalances: BalanceType[]; }; export function Balance({ showBalances }: BalanceProps) { + const [username, setUsername] = useState(); + const [address, setAddress] = useState(); const { controller } = useController(); const { balance: creditBalance } = useCreditBalance({ - username: controller?.username(), + username: username, interval: 3000, }); + const { data: [eth], } = useERC20Balance({ - address: controller?.address, + address: address, contractAddress: ETH_CONTRACT_ADDRESS, provider: controller, interval: 3000, @@ -43,6 +51,17 @@ export function Balance({ showBalances }: BalanceProps) { { enabled: !!eth }, ); + useEffect(() => { + const activeController = !isIframe() + ? Controller.fromStore(import.meta.env.VITE_ORIGIN!) + : controller; + + if (activeController) { + setUsername(activeController.username()); + setAddress(activeController.address); + } + }, [controller]); + return ( diff --git a/packages/keychain/src/components/funding/PurchaseCredits.tsx b/packages/keychain/src/components/funding/PurchaseCredits.tsx index 4cf267d82..52ad77c22 100644 --- a/packages/keychain/src/components/funding/PurchaseCredits.tsx +++ b/packages/keychain/src/components/funding/PurchaseCredits.tsx @@ -82,24 +82,6 @@ export function PurchaseCredits({ onBack }: PurchaseCreditsProps) { }, } as Appearance; - // For when we need to support Payment Links - // useStripePaymentQuery( - // { referenceId }, - // { - // enabled: !!referenceId && !error, - // refetchInterval: REFETCH_INTERVAL, - // retry: MAX_RETRIES, - // onSuccess: () => setState(PurchaseState.SUCCESS), - // onError: () => { - // setError( - // new Error( - // `Payment not received. Please try again. Reference ID: ${referenceId}`, - // ), - // ); - // }, - // }, - // ); - if (state === PurchaseState.STRIPE_CHECKOUT) { return ( diff --git a/packages/keychain/src/components/funding/index.tsx b/packages/keychain/src/components/funding/index.tsx index 582ed3c26..e401fcb9f 100644 --- a/packages/keychain/src/components/funding/index.tsx +++ b/packages/keychain/src/components/funding/index.tsx @@ -10,7 +10,7 @@ import { } from "@cartridge/ui-next"; import { DepositEth } from "./DepositEth"; import { PurchaseCredits } from "./PurchaseCredits"; -import { Balance } from "./Balance"; +import { Balance, BalanceType } from "./Balance"; const enum FundingState { SHOW_OPTIONS, @@ -19,15 +19,20 @@ const enum FundingState { } export type FundingProps = { - title?: React.ReactElement; + title?: React.ReactElement | string; + creditsOnly?: boolean; onComplete?: (deployHash?: string) => void; }; -export function Funding({ onComplete, title }: FundingProps) { +export function Funding({ title, creditsOnly, onComplete }: FundingProps) { const { controller } = useConnection(); const [state, setState] = useState(FundingState.SHOW_OPTIONS); + const showBalances: BalanceType[] = creditsOnly + ? ["credits"] + : ["credits", "eth"]; const showCredits = - typeof document !== "undefined" && document.cookie.includes("credits="); + (typeof document !== "undefined" && document.cookie.includes("credits=")) || + creditsOnly; if (state === FundingState.FUND_ETH) { return ( @@ -49,9 +54,10 @@ export function Funding({ onComplete, title }: FundingProps) { title={title || (controller ? `Fund ${controller.username()}` : "")} description={controller && } icon={} + hideNetwork > - +
{showCredits && ( @@ -59,12 +65,14 @@ export function Funding({ onComplete, title }: FundingProps) { Purchase Credits )} - + {!creditsOnly && ( + + )}
); diff --git a/packages/keychain/src/components/slot/consent.tsx b/packages/keychain/src/components/slot/consent.tsx index 29bc8b803..14194e1a8 100644 --- a/packages/keychain/src/components/slot/consent.tsx +++ b/packages/keychain/src/components/slot/consent.tsx @@ -2,10 +2,11 @@ import Controller from "@/utils/controller"; import { Button } from "@cartridge/ui-next"; import { Container, Footer } from "@/components/layout"; import { useCallback, useEffect } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; export function Consent() { const navigate = useNavigate(); + const { pathname } = useLocation(); const [searchParams] = useSearchParams(); const callback_uri = searchParams.get("callback_uri")!; @@ -25,9 +26,18 @@ export function Consent() { useEffect(() => { if (!Controller.fromStore(import.meta.env.VITE_ORIGIN!)) { - navigate("/slot", { replace: true }); + navigate( + `/slot?returnTo=${encodeURIComponent(pathname)}${ + callback_uri + ? `&callback_uri=${encodeURIComponent(callback_uri)}` + : "" + }`, + { + replace: true, + }, + ); } - }, [navigate]); + }, [navigate, callback_uri, pathname]); return ( { + if (!Controller.fromStore(import.meta.env.VITE_ORIGIN!)) { + navigate(`/slot?returnTo=${encodeURIComponent(pathname)}`, { + replace: true, + }); + } + }, [navigate, pathname]); + + return ; +} diff --git a/packages/keychain/src/components/slot/index.tsx b/packages/keychain/src/components/slot/index.tsx index 84da58f69..afcb117bd 100644 --- a/packages/keychain/src/components/slot/index.tsx +++ b/packages/keychain/src/components/slot/index.tsx @@ -23,6 +23,7 @@ export function Slot() { case "/slot/auth/failure": return ; case "/slot/consent": + case "/slot/fund": return ; default: return ; @@ -37,13 +38,17 @@ function Auth() { useEffect(() => { if (user && controller) { - const query = Array.from(searchParams.entries()).reduce( - (prev, [key, val], i) => - i === 0 ? `?${key}=${val}` : `${prev}&${key}=${val}`, - "", - ); + const returnTo = searchParams.get("returnTo"); + const otherParams = Array.from(searchParams.entries()) + .filter(([key]) => key !== "returnTo") + .reduce( + (prev, [key, val], i) => + i === 0 ? `?${key}=${val}` : `${prev}&${key}=${val}`, + "", + ); - navigate(`/slot/consent${query}`, { replace: true }); + const target = returnTo ? `${returnTo}${otherParams}` : "/slot"; + navigate(target, { replace: true }); } }, [user, controller, navigate, searchParams]);