From e4590a7adaa1298eb5e3a73044328c0f34c4977c Mon Sep 17 00:00:00 2001 From: MananTank Date: Tue, 15 Apr 2025 19:08:18 +0000 Subject: [PATCH] [NEB-168] Nebula: Improve Execute Transaction Card UI (#6731) ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/yNOf1svJ8o3zjO7zQouZ/5807d025-2166-4b28-bedb-2baccd52054c.png) --- .../@/components/blocks/NetworkSelectors.tsx | 6 +- .../@/components/blocks/wallet-address.tsx | 18 +- .../_layout/metadata-header.tsx | 4 +- .../cross-chain/single-network-selector.tsx | 4 +- .../ExecuteTransactionCard.stories.tsx | 4 +- .../components/ExecuteTransactionCard.tsx | 217 ++++++++++++------ .../contract-subscriptions-table.tsx | 9 +- .../components/backend-wallets-table.tsx | 7 +- .../components/transactions-table.tsx | 6 +- .../relayers/components/relayers-table.tsx | 6 +- .../SponsoredTransactionsTableUI.tsx | 4 +- .../src/components/cmd-k-search/index.tsx | 4 +- .../ConfigureNetworkForm.tsx | 7 +- .../tables/contract-table.tsx | 4 +- .../src/components/icons/ChainIcon.tsx | 2 +- .../selects/CustomChainRenderer.tsx | 4 +- .../selects/NetworkSelectDropdown.tsx | 6 +- .../selects/NetworkSelectorButton.tsx | 4 +- .../settings/Account/Billing/CreditsItem.tsx | 4 +- 19 files changed, 203 insertions(+), 117 deletions(-) diff --git a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx index 5d6a2559256..e8ccfe01a56 100644 --- a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx +++ b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx @@ -4,7 +4,7 @@ import { MultiSelect } from "@/components/blocks/multi-select"; import { SelectWithSearch } from "@/components/blocks/select-with-search"; import { Badge } from "@/components/ui/badge"; import { useCallback, useMemo } from "react"; -import { ChainIcon } from "../../../components/icons/ChainIcon"; +import { ChainIconClient } from "../../../components/icons/ChainIcon"; import { useAllChainsData } from "../../../hooks/chains/allChains"; function cleanChainName(chainName: string) { @@ -77,7 +77,7 @@ export function MultiNetworkSelector(props: { return (
- - )} @@ -168,6 +170,7 @@ function WalletAvatar(props: { address: string; profiles: SocialProfile[]; thirdwebClient: ThirdwebClient; + iconClassName?: string; }) { const avatar = useMemo(() => { return props.profiles.find( @@ -186,11 +189,20 @@ function WalletAvatar(props: { : undefined; return ( -
+
{resolvedAvatarSrc ? ( - + ) : ( - + )}
); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx index b1257ffcdc0..e398f70649f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx @@ -3,7 +3,7 @@ import { CopyAddressButton } from "@/components/ui/CopyAddressButton"; import { Button } from "@/components/ui/button"; import { getThirdwebClient } from "@/constants/thirdweb.server"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { ExternalLinkIcon } from "lucide-react"; import Link from "next/link"; import type { ChainMetadata } from "thirdweb/chains"; @@ -85,7 +85,7 @@ export const MetadataHeader: React.FC = ({ href={`/${chain.slug}`} className="flex w-fit shrink-0 items-center gap-2 rounded-3xl border border-border bg-card px-2.5 py-1.5 hover:bg-accent" > - + {cleanedChainName && ( {cleanedChainName} )} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/single-network-selector.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/single-network-selector.tsx index 3f05391fdea..fc5a848cb89 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/single-network-selector.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/single-network-selector.tsx @@ -1,6 +1,6 @@ import { SelectWithSearch } from "@/components/blocks/select-with-search"; import { Badge } from "@/components/ui/badge"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { useAllChainsData } from "hooks/chains/allChains"; import { useCallback, useMemo } from "react"; @@ -45,7 +45,7 @@ export function SingleNetworkSelector(props: { return (
- diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx index 20fc09a8a6c..ca484cd459f 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx @@ -1,7 +1,8 @@ +import { WalletAddress } from "@/components/blocks/wallet-address"; import { CopyTextButton } from "@/components/ui/CopyTextButton"; import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Button } from "@/components/ui/button"; -import { CodeClient } from "@/components/ui/code/code.client"; +import { cn } from "@/lib/utils"; import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi"; import { ArrowRightLeftIcon, @@ -15,10 +16,12 @@ import { useState } from "react"; import { type ThirdwebClient, prepareTransaction, + toEther, waitForReceipt, } from "thirdweb"; -import { useSendTransaction } from "thirdweb/react"; +import { useActiveAccount, useSendTransaction } from "thirdweb/react"; import { TransactionButton } from "../../../../components/buttons/TransactionButton"; +import { ChainIconClient } from "../../../../components/icons/ChainIcon"; import { useTrack } from "../../../../hooks/analytics/useTrack"; import { useV5DashboardChain } from "../../../../lib/v5-adapter"; import { getSDKTheme } from "../../../components/sdk-component-theme"; @@ -76,29 +79,157 @@ export function ExecuteTransactionCardLayout(props: { }, }); const chain = useV5DashboardChain(txData.chainId); - const isTransactionSent = - props.status.type === "confirming" || props.status.type === "confirmed"; const trackEvent = useTrack(); + const account = useActiveAccount(); const explorer = chain.blockExplorers?.[0]?.url; + const isTransactionPending = + props.status.type === "sending" || props.status.type === "confirming"; + return (
-
-

- Transaction -

+ {/* header */} +

+ Transaction +

+ + {/* content */} +
+ {/* From */} +
+ From + {account ? ( + + ) : ( + Your Wallet + )} +
+ + {/* To */} + {txData.to && ( +
+ To + + +
+ )} + + {/* Value */} +
+ Value + {toEther(BigInt(txData.value))} {chain.nativeCurrency?.symbol} +
+ + {/* Network */} +
+ Network +
+ + + {chain.name || `Chain ID: ${txData.chainId}`} + +
+
+ + {/* Status */} + {props.status.type !== "idle" && ( +
+ Status +
+ + {/* icon */} + {(props.status.type === "sending" || + props.status.type === "confirming") && ( + + )} - + {props.status.type === "confirmed" && ( + + )} + + {props.status.type === "failed" && ( + + )} + + {/* text */} + + {props.status.type === "sending" && "Sending Transaction"} + {props.status.type === "confirming" && + "Waiting for Confirmation"} + {props.status.type === "confirmed" && + "Transaction Confirmed"} + {props.status.type === "failed" && "Transaction Failed"} + + +
+
+ )} + + {/* Transaction Hash */} + {"txHash" in props.status && props.status.txHash && ( +
+ Transaction Hash +
+ {explorer ? ( + + ) : ( + + )} +
+
+ )}
-
+ {/* footer */} +
{ trackEvent({ category: "nebula", @@ -164,72 +295,6 @@ export function ExecuteTransactionCardLayout(props: {
- - {/* Tx Status */} - {props.status.type !== "idle" && ( -
-
- {props.status.type === "sending" && ( -
- -

Sending Transaction

-
- )} - - {isTransactionSent && ( -
- -

Transaction Sent

-
- )} - - {props.status.type === "confirming" && ( -
- -

Confirming Transaction

-
- )} - - {props.status.type === "confirmed" && ( -
- -

Transaction Confirmed

-
- )} - - {props.status.type === "failed" && ( -
- -

Transaction Failed

-
- )} -
- - {"txHash" in props.status && props.status.txHash && ( -
- {explorer && ( - - )} - -
- )} -
- )}
); } diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx index c711406d35d..9b7831adf09 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx @@ -23,7 +23,7 @@ import { } from "@chakra-ui/react"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { createColumnHelper } from "@tanstack/react-table"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { TWTable } from "components/shared/TWTable"; import { format } from "date-fns"; import { useTrack } from "hooks/analytics/useTrack"; @@ -81,7 +81,7 @@ export const ContractSubscriptionTable: React.FC< const chain = idToChain.get(cell.getValue()); return ( - + {chain?.name ?? "N/A"} ); @@ -399,7 +399,10 @@ const RemoveModal = ({ Chain - + {chain?.name ?? "N/A"} diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx index 7dcfe515342..ef00a106e6f 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx @@ -34,7 +34,7 @@ import { } from "@chakra-ui/react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { type ColumnDef, createColumnHelper } from "@tanstack/react-table"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { TWTable } from "components/shared/TWTable"; import { useTrack } from "hooks/analytics/useTrack"; import { useAllChainsData } from "hooks/chains/allChains"; @@ -582,7 +582,10 @@ const SendFundsModal = ({ Chain - + {chain?.name} diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx index 725a0cd2d3f..230a2e9b7c1 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx @@ -38,7 +38,7 @@ import { useEngineTransactions, } from "@3rdweb-sdk/react/hooks/useEngine"; import { Collapse, Divider, useDisclosure } from "@chakra-ui/react"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { formatDistanceToNowStrict } from "date-fns"; import { format, formatDate } from "date-fns/format"; import { useAllChainsData } from "hooks/chains/allChains"; @@ -373,7 +373,7 @@ function TxChainCell(props: { chainId: string | undefined }) { return (
- +
{chain.name ?? `Chain ID: ${chainId}`}
@@ -622,7 +622,7 @@ const TransactionDetailsDrawer = ({
Chain
- + {chain?.name}
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/relayers/components/relayers-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/relayers/components/relayers-table.tsx index a31782c0fc5..8704f01834d 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/relayers/components/relayers-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/relayers/components/relayers-table.tsx @@ -23,7 +23,7 @@ import { useDisclosure, } from "@chakra-ui/react"; import { createColumnHelper } from "@tanstack/react-table"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { TWTable } from "components/shared/TWTable"; import { useTrack } from "hooks/analytics/useTrack"; import { useAllChainsData } from "hooks/chains/allChains"; @@ -71,7 +71,7 @@ export const RelayersTable: React.FC = ({ const chain = idToChain.get(Number.parseInt(cell.getValue())); return ( - + {chain?.name ?? "N/A"} ); @@ -404,7 +404,7 @@ const RemoveModal = ({ Chain - - + = ({ isActive && "bg-muted", )} > - diff --git a/apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx b/apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx index 52b618b3afe..6302696a924 100644 --- a/apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx +++ b/apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx @@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { useStore } from "@/lib/reactive"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { getDashboardChainRpc } from "lib/rpc"; import { CircleAlertIcon, Trash2Icon } from "lucide-react"; import { useForm } from "react-hook-form"; @@ -369,7 +369,10 @@ export const ConfigureNetworkForm: React.FC = ({ label="Icon" >
- + { form.setValue("icon", uri, { shouldDirty: true }); diff --git a/apps/dashboard/src/components/contract-components/tables/contract-table.tsx b/apps/dashboard/src/components/contract-components/tables/contract-table.tsx index ab796fd688b..842d1389635 100644 --- a/apps/dashboard/src/components/contract-components/tables/contract-table.tsx +++ b/apps/dashboard/src/components/contract-components/tables/contract-table.tsx @@ -20,7 +20,7 @@ import { TableRow, } from "@/components/ui/table"; import { useMutation } from "@tanstack/react-query"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { NetworkSelectDropdown } from "components/selects/NetworkSelectDropdown"; import { EllipsisVerticalIcon, ExternalLinkIcon, XIcon } from "lucide-react"; import Link from "next/link"; @@ -263,7 +263,7 @@ const ChainNameCell = React.memo(function ChainNameCell(props: { `Unknown Network (#${props.chainId})`; return (
- + { +export const ChainIconClient = ({ ipfsSrc, ...restProps }: ChainIconProps) => { const src = ipfsSrc ? replaceIpfsUrl(ipfsSrc) : fallbackChainIcon; return ( diff --git a/apps/dashboard/src/components/selects/CustomChainRenderer.tsx b/apps/dashboard/src/components/selects/CustomChainRenderer.tsx index 629d68828c6..7d9a28a90b9 100644 --- a/apps/dashboard/src/components/selects/CustomChainRenderer.tsx +++ b/apps/dashboard/src/components/selects/CustomChainRenderer.tsx @@ -1,7 +1,7 @@ import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { OPSponsoredChains } from "constants/chains"; import { SettingsIcon } from "lucide-react"; import type { UseNetworkSwitcherModalOptions } from "thirdweb/react"; @@ -49,7 +49,7 @@ export const CustomChainRenderer = ({ } }} > - +

= ({ >

- + All Networks
@@ -75,7 +75,7 @@ export const NetworkSelectDropdown: React.FC = ({ {chains.map((chain) => (
- + {useCleanChainName ? cleanChainName(chain.name) : chain.name}
diff --git a/apps/dashboard/src/components/selects/NetworkSelectorButton.tsx b/apps/dashboard/src/components/selects/NetworkSelectorButton.tsx index 7b1d0b82d90..ef6b3dab0e9 100644 --- a/apps/dashboard/src/components/selects/NetworkSelectorButton.tsx +++ b/apps/dashboard/src/components/selects/NetworkSelectorButton.tsx @@ -5,7 +5,7 @@ import { useThirdwebClient } from "@/constants/thirdweb.client"; import { useStore } from "@/lib/reactive"; import { cn } from "@/lib/utils"; import { popularChains } from "@3rdweb-sdk/react/components/popularChains"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { useActiveChainAsDashboardChain } from "lib/v5-adapter"; import { ChevronDownIcon } from "lucide-react"; import { useTheme } from "next-themes"; @@ -199,7 +199,7 @@ export const NetworkSelectorButton: React.FC = ({ }); }} > - + {chain?.name || "Select Network"} diff --git a/apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx b/apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx index 47c23934def..d81a18ed7e6 100644 --- a/apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx +++ b/apps/dashboard/src/components/settings/Account/Billing/CreditsItem.tsx @@ -1,7 +1,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import type { Account, BillingCredit } from "@3rdweb-sdk/react/hooks/useApi"; -import { ChainIcon } from "components/icons/ChainIcon"; +import { ChainIconClient } from "components/icons/ChainIcon"; import { formatDistance } from "date-fns"; import { useTrack } from "hooks/analytics/useTrack"; import { useLocalStorage } from "hooks/useLocalStorage"; @@ -49,7 +49,7 @@ export const CreditsItem: React.FC = ({
{isOpCredit ? ( -