From cf166e4c04552a18f4341888c00e8593f3ef4a5a Mon Sep 17 00:00:00 2001 From: MananTank Date: Tue, 4 Feb 2025 01:03:52 +0000 Subject: [PATCH] [TOOL-3216] Dashboard: Add Faucet Refill, Fix toast colors on light mode (#6147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR focuses on improving the UI and functionality of the dashboard by updating theme management, enhancing the `FaucetButton` component, and integrating a new modal for refilling the faucet. It also modifies color schemes for buttons and introduces a loading state. ### Detailed summary - Removed `` from `RootLayout`. - Added `` component in `AppRouterProviders` for dynamic theming. - Updated button color schemes in `sdk-component-theme.ts`. - Enhanced `FaucetButton` with loading states and a new modal for refilling. - Introduced `SendFundsToFaucetModalButton` and `SendFundsToFaucetModalContent` components. - Added form validation for the amount in the faucet refill modal. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .../components/client/FaucetButton.tsx | 246 ++++++++++++++++-- .../src/app/components/sdk-component-theme.ts | 4 +- apps/dashboard/src/app/layout.tsx | 2 - apps/dashboard/src/app/providers.tsx | 9 +- 4 files changed, 235 insertions(+), 26 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx index 7749389704e..48264ebd066 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx @@ -1,13 +1,32 @@ "use client"; +import { CopyTextButton } from "@/components/ui/CopyTextButton"; import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Button } from "@/components/ui/button"; -import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { THIRDWEB_ENGINE_FAUCET_WALLET, TURNSTILE_SITE_KEY, } from "@/constants/env"; import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; +import { zodResolver } from "@hookform/resolvers/zod"; import { Turnstile } from "@marsidev/react-turnstile"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType"; @@ -17,9 +36,21 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; -import { toUnits } from "thirdweb"; +import { + type ThirdwebClient, + prepareTransaction, + toUnits, + toWei, +} from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; -import { useActiveAccount, useWalletBalance } from "thirdweb/react"; +import type { Chain } from "thirdweb/chains"; +import { + useActiveAccount, + useActiveWalletChain, + useSendTransaction, + useSwitchActiveWalletChain, + useWalletBalance, +} from "thirdweb/react"; import { z } from "zod"; import { isOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired"; @@ -137,6 +168,35 @@ export function FaucetButton({ const form = useForm>(); + // loading state + if (faucetWalletBalanceQuery.isPending || canClaimFaucetQuery.isPending) { + return ( + + ); + } + + // faucet is empty + if (isFaucetEmpty) { + return ( +
+
+ Faucet is empty right now +
+ { + faucetWalletBalanceQuery.refetch(); + }} + /> +
+ ); + } + // Force users to log in to claim the faucet if (!address || !twAccount) { return ( @@ -164,24 +224,6 @@ export function FaucetButton({ ); } - // loading state - if (faucetWalletBalanceQuery.isPending || canClaimFaucetQuery.isPending) { - return ( - - ); - } - - // faucet is empty - if (isFaucetEmpty) { - return ( - - ); - } - // Can not claim if (canClaimFaucetQuery.data && canClaimFaucetQuery.data.canClaim === false) { return ( @@ -250,3 +292,165 @@ export function FaucetButton({ ); } + +const faucetFormSchema = z.object({ + amount: z.coerce.number().refine((value) => value > 0, { + message: "Amount must be greater than 0", + }), +}); + +function SendFundsToFaucetModalButton(props: { + chain: Chain; + isLoggedIn: boolean; + client: ThirdwebClient; + chainMeta: ChainMetadata; + onFaucetRefill: () => void; +}) { + return ( + + + + + + + Refill Faucet + Send funds to faucet wallet + + + + + + ); +} + +function SendFundsToFaucetModalContent(props: { + chain: Chain; + isLoggedIn: boolean; + client: ThirdwebClient; + chainMeta: ChainMetadata; + onFaucetRefill: () => void; +}) { + const account = useActiveAccount(); + const activeChain = useActiveWalletChain(); + const switchActiveWalletChain = useSwitchActiveWalletChain(); + const sendTxMutation = useSendTransaction({ + payModal: false, + }); + const switchChainMutation = useMutation({ + mutationFn: async () => { + await switchActiveWalletChain(props.chain); + }, + }); + + const form = useForm>({ + resolver: zodResolver(faucetFormSchema), + defaultValues: { + amount: 0.1, + }, + }); + + function onSubmit(values: z.infer) { + const sendNativeTokenTx = prepareTransaction({ + chain: props.chain, + client: props.client, + to: THIRDWEB_ENGINE_FAUCET_WALLET, + value: toWei(values.amount.toString()), + }); + + const promise = sendTxMutation.mutateAsync(sendNativeTokenTx); + + toast.promise(promise, { + success: `Sent ${values.amount} ${props.chainMeta.nativeCurrency.symbol} to faucet`, + error: `Failed to send ${values.amount} ${props.chainMeta.nativeCurrency.symbol} to faucet`, + }); + + promise.then(() => { + props.onFaucetRefill(); + }); + } + + return ( +
+ +
+

Faucet Wallet

+ +
+ + ( + + Amount + +
+ +
+ {props.chainMeta.nativeCurrency.symbol} +
+
+
+ + +
+ )} + /> + + {!account && ( + + )} + + {account && activeChain && ( +
+ {activeChain.id === props.chain.id ? ( + + ) : ( + + )} +
+ )} + + + ); +} diff --git a/apps/dashboard/src/app/components/sdk-component-theme.ts b/apps/dashboard/src/app/components/sdk-component-theme.ts index 391d6dd5d89..179c58dbab1 100644 --- a/apps/dashboard/src/app/components/sdk-component-theme.ts +++ b/apps/dashboard/src/app/components/sdk-component-theme.ts @@ -25,8 +25,8 @@ export function getSDKTheme(theme: "light" | "dark"): Theme { accentText: "hsl(var(--link-foreground))", accentButtonBg: "hsl(var(--primary))", accentButtonText: "hsl(var(--primary-foreground))", - primaryButtonBg: "hsl(var(--background))", - primaryButtonText: "hsl(var(--foreground))", + primaryButtonBg: "hsl(var(--inverted))", + primaryButtonText: "hsl(var(--inverted-foreground))", secondaryButtonText: "hsl(var(--secondary-foreground))", tooltipBg: "hsl(var(--popover))", tooltipText: "hsl(var(--popover-foreground))", diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index a1fe198299d..0485963dd5c 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -1,5 +1,4 @@ import "@/styles/globals.css"; -import { Toaster } from "@/components/ui/sonner"; import { DashboardRouterTopProgressBar } from "@/lib/DashboardRouter"; import { cn } from "@/lib/utils"; import type { Metadata } from "next"; @@ -82,7 +81,6 @@ export default function RootLayout({ - + {props.children} @@ -40,6 +42,11 @@ export function AppRouterProviders(props: { children: React.ReactNode }) { ); } +function ToasterSetup() { + const { theme } = useTheme(); + return ; +} + function SyncChainDefinitionsToConnectionManager() { const { allChainsV5 } = useAllChainsData(); const connectionManager = useConnectionManager();