Skip to content

Commit e4590a7

Browse files
committed
1 parent a45c558 commit e4590a7

File tree

19 files changed

+203
-117
lines changed

19 files changed

+203
-117
lines changed

apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { MultiSelect } from "@/components/blocks/multi-select";
44
import { SelectWithSearch } from "@/components/blocks/select-with-search";
55
import { Badge } from "@/components/ui/badge";
66
import { useCallback, useMemo } from "react";
7-
import { ChainIcon } from "../../../components/icons/ChainIcon";
7+
import { ChainIconClient } from "../../../components/icons/ChainIcon";
88
import { useAllChainsData } from "../../../hooks/chains/allChains";
99

1010
function cleanChainName(chainName: string) {
@@ -77,7 +77,7 @@ export function MultiNetworkSelector(props: {
7777
return (
7878
<div className="flex justify-between gap-4">
7979
<span className="flex grow gap-2 truncate text-left">
80-
<ChainIcon
80+
<ChainIconClient
8181
className="size-5"
8282
ipfsSrc={chain.icon?.url}
8383
loading="lazy"
@@ -172,7 +172,7 @@ export function SingleNetworkSelector(props: {
172172
return (
173173
<div className="flex justify-between gap-4">
174174
<span className="flex grow gap-2 truncate text-left">
175-
<ChainIcon
175+
<ChainIconClient
176176
className="size-5"
177177
ipfsSrc={chain.icon?.url}
178178
loading="lazy"

apps/dashboard/src/@/components/blocks/wallet-address.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export function WalletAddress(props: {
2323
address: string | undefined;
2424
shortenAddress?: boolean;
2525
className?: string;
26+
iconClassName?: string;
2627
}) {
2728
const thirdwebClient = useThirdwebClient();
2829
// default back to zero address if no address provided
@@ -78,6 +79,7 @@ export function WalletAddress(props: {
7879
address={address}
7980
profiles={profiles.data || []}
8081
thirdwebClient={thirdwebClient}
82+
iconClassName={props.iconClassName}
8183
/>
8284
)}
8385
<span className="cursor-pointer font-mono">
@@ -168,6 +170,7 @@ function WalletAvatar(props: {
168170
address: string;
169171
profiles: SocialProfile[];
170172
thirdwebClient: ThirdwebClient;
173+
iconClassName?: string;
171174
}) {
172175
const avatar = useMemo(() => {
173176
return props.profiles.find(
@@ -186,11 +189,20 @@ function WalletAvatar(props: {
186189
: undefined;
187190

188191
return (
189-
<div className="size-6 overflow-hidden rounded-full">
192+
<div
193+
className={cn("size-6 overflow-hidden rounded-full", props.iconClassName)}
194+
>
190195
{resolvedAvatarSrc ? (
191-
<Img src={resolvedAvatarSrc} className="size-6 object-cover" />
196+
<Img
197+
src={resolvedAvatarSrc}
198+
className={cn("size-6 object-cover", props.iconClassName)}
199+
/>
192200
) : (
193-
<Blobbie address={props.address} size={24} />
201+
<Blobbie
202+
address={props.address}
203+
size={24}
204+
className={props.iconClassName}
205+
/>
194206
)}
195207
</div>
196208
);

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
44
import { Button } from "@/components/ui/button";
55
import { getThirdwebClient } from "@/constants/thirdweb.server";
6-
import { ChainIcon } from "components/icons/ChainIcon";
6+
import { ChainIconClient } from "components/icons/ChainIcon";
77
import { ExternalLinkIcon } from "lucide-react";
88
import Link from "next/link";
99
import type { ChainMetadata } from "thirdweb/chains";
@@ -85,7 +85,7 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
8585
href={`/${chain.slug}`}
8686
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"
8787
>
88-
<ChainIcon ipfsSrc={chain.icon?.url} className="size-4" />
88+
<ChainIconClient ipfsSrc={chain.icon?.url} className="size-4" />
8989
{cleanedChainName && (
9090
<span className="text-xs">{cleanedChainName}</span>
9191
)}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/single-network-selector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SelectWithSearch } from "@/components/blocks/select-with-search";
22
import { Badge } from "@/components/ui/badge";
3-
import { ChainIcon } from "components/icons/ChainIcon";
3+
import { ChainIconClient } from "components/icons/ChainIcon";
44
import { useAllChainsData } from "hooks/chains/allChains";
55
import { useCallback, useMemo } from "react";
66

@@ -45,7 +45,7 @@ export function SingleNetworkSelector(props: {
4545
return (
4646
<div className="flex justify-between gap-4">
4747
<span className="flex grow gap-2 truncate text-left">
48-
<ChainIcon
48+
<ChainIconClient
4949
className="size-5"
5050
ipfsSrc={chain.icon?.url}
5151
loading="lazy"

apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ function Variant(props: {
7171
twAccount={accountStub()}
7272
txData={{
7373
chainId: 1,
74-
to: "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37",
74+
to: "0xEb0effdFB4dC5b3d5d3aC6ce29F3ED213E95d675", // thirdweb.eth
7575
data: "0x",
76-
value: "0x16345785d8a0000",
76+
value: "0x16345785d8a0000", // 0.1 eth
7777
}}
7878
/>
7979
</BadgeContainer>

apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx

Lines changed: 141 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { WalletAddress } from "@/components/blocks/wallet-address";
12
import { CopyTextButton } from "@/components/ui/CopyTextButton";
23
import { Spinner } from "@/components/ui/Spinner/Spinner";
34
import { Button } from "@/components/ui/button";
4-
import { CodeClient } from "@/components/ui/code/code.client";
5+
import { cn } from "@/lib/utils";
56
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
67
import {
78
ArrowRightLeftIcon,
@@ -15,10 +16,12 @@ import { useState } from "react";
1516
import {
1617
type ThirdwebClient,
1718
prepareTransaction,
19+
toEther,
1820
waitForReceipt,
1921
} from "thirdweb";
20-
import { useSendTransaction } from "thirdweb/react";
22+
import { useActiveAccount, useSendTransaction } from "thirdweb/react";
2123
import { TransactionButton } from "../../../../components/buttons/TransactionButton";
24+
import { ChainIconClient } from "../../../../components/icons/ChainIcon";
2225
import { useTrack } from "../../../../hooks/analytics/useTrack";
2326
import { useV5DashboardChain } from "../../../../lib/v5-adapter";
2427
import { getSDKTheme } from "../../../components/sdk-component-theme";
@@ -76,29 +79,157 @@ export function ExecuteTransactionCardLayout(props: {
7679
},
7780
});
7881
const chain = useV5DashboardChain(txData.chainId);
79-
const isTransactionSent =
80-
props.status.type === "confirming" || props.status.type === "confirmed";
8182
const trackEvent = useTrack();
83+
const account = useActiveAccount();
8284

8385
const explorer = chain.blockExplorers?.[0]?.url;
8486

87+
const isTransactionPending =
88+
props.status.type === "sending" || props.status.type === "confirming";
89+
8590
return (
8691
<div>
8792
<div className="rounded-xl border bg-card">
88-
<div className="flex flex-col gap-4 p-4 pb-6">
89-
<h3 className="font-semibold text-foreground text-lg tracking-tight">
90-
Transaction
91-
</h3>
93+
{/* header */}
94+
<h3 className="border-b p-4 py-4 font-semibold text-foreground text-lg tracking-tight lg:px-6 lg:text-xl">
95+
Transaction
96+
</h3>
97+
98+
{/* content */}
99+
<div className="px-4 text-sm lg:px-6 [&>*]:h-12 [&>*]:border-b lg:[&>*]:h-14">
100+
{/* From */}
101+
<div className="flex items-center justify-between gap-2">
102+
<span className="text-muted-foreground">From</span>
103+
{account ? (
104+
<WalletAddress
105+
address={account.address}
106+
className="h-auto py-0"
107+
iconClassName="size-5"
108+
/>
109+
) : (
110+
<span className="text-muted-foreground">Your Wallet</span>
111+
)}
112+
</div>
113+
114+
{/* To */}
115+
{txData.to && (
116+
<div className="flex items-center justify-between gap-2">
117+
<span className="text-muted-foreground">To</span>
118+
119+
<WalletAddress
120+
address={txData.to}
121+
className="h-auto py-0"
122+
iconClassName="size-5"
123+
/>
124+
</div>
125+
)}
126+
127+
{/* Value */}
128+
<div className="flex items-center justify-between gap-2">
129+
<span className="text-muted-foreground">Value</span>
130+
{toEther(BigInt(txData.value))} {chain.nativeCurrency?.symbol}
131+
</div>
132+
133+
{/* Network */}
134+
<div className="flex items-center justify-between gap-2">
135+
<span className="text-muted-foreground">Network</span>
136+
<div className="flex items-center gap-2">
137+
<ChainIconClient
138+
className="size-5 rounded-full"
139+
src={chain.icon?.url}
140+
/>
141+
<span className="text-foreground">
142+
{chain.name || `Chain ID: ${txData.chainId}`}
143+
</span>
144+
</div>
145+
</div>
146+
147+
{/* Status */}
148+
{props.status.type !== "idle" && (
149+
<div className="flex items-center justify-between gap-2">
150+
<span className="text-muted-foreground">Status</span>
151+
<div className="flex items-center gap-2">
152+
<span
153+
className={cn(
154+
"flex items-center gap-2 font-medium",
155+
props.status.type === "sending" && "text-blue-500",
156+
props.status.type === "confirming" && "text-yellow-500",
157+
props.status.type === "confirmed" && "text-green-500",
158+
props.status.type === "failed" && "text-red-500",
159+
)}
160+
>
161+
{/* icon */}
162+
{(props.status.type === "sending" ||
163+
props.status.type === "confirming") && (
164+
<Spinner className="size-4" />
165+
)}
92166

93-
<CodeClient code={JSON.stringify(txData, null, 2)} lang="json" />
167+
{props.status.type === "confirmed" && (
168+
<CircleCheckIcon className="size-4" />
169+
)}
170+
171+
{props.status.type === "failed" && (
172+
<CircleXIcon className="size-4" />
173+
)}
174+
175+
{/* text */}
176+
<span>
177+
{props.status.type === "sending" && "Sending Transaction"}
178+
{props.status.type === "confirming" &&
179+
"Waiting for Confirmation"}
180+
{props.status.type === "confirmed" &&
181+
"Transaction Confirmed"}
182+
{props.status.type === "failed" && "Transaction Failed"}
183+
</span>
184+
</span>
185+
</div>
186+
</div>
187+
)}
188+
189+
{/* Transaction Hash */}
190+
{"txHash" in props.status && props.status.txHash && (
191+
<div className="flex items-center justify-between gap-1">
192+
<span className="text-muted-foreground">Transaction Hash</span>
193+
<div className="flex justify-end gap-2.5">
194+
{explorer ? (
195+
<Button
196+
asChild
197+
variant="ghost"
198+
size="sm"
199+
className="gap-1.5 font-mono"
200+
>
201+
<Link
202+
href={`${explorer}/tx/${props.status.txHash}`}
203+
target="_blank"
204+
>
205+
{`${props.status.txHash.slice(0, 6)}...${props.status.txHash.slice(-4)}`}
206+
<ExternalLinkIcon className="size-3" />
207+
</Link>
208+
</Button>
209+
) : (
210+
<CopyTextButton
211+
textToCopy={props.status.txHash}
212+
textToShow={`${props.status.txHash.slice(0, 6)}...${props.status.txHash.slice(-4)}`}
213+
variant="ghost"
214+
className="font-mono"
215+
copyIconPosition="right"
216+
tooltip="Copy Transaction Hash"
217+
/>
218+
)}
219+
</div>
220+
</div>
221+
)}
94222
</div>
95223

96-
<div className="flex items-center justify-end border-t p-4">
224+
{/* footer */}
225+
<div className="flex items-center justify-end px-4 py-6 lg:px-6">
97226
<TransactionButton
98227
isPending={sendTransaction.isPending}
99228
transactionCount={undefined}
100229
txChainID={txData.chainId}
101230
variant="default"
231+
disabled={isTransactionPending}
232+
size="sm"
102233
onClick={async () => {
103234
trackEvent({
104235
category: "nebula",
@@ -164,72 +295,6 @@ export function ExecuteTransactionCardLayout(props: {
164295
</TransactionButton>
165296
</div>
166297
</div>
167-
168-
{/* Tx Status */}
169-
{props.status.type !== "idle" && (
170-
<div className="mt-5 rounded-lg border bg-card">
171-
<div className="flex flex-col gap-1.5 p-4">
172-
{props.status.type === "sending" && (
173-
<div className="flex items-center gap-2 text-link-foreground">
174-
<Spinner className="size-4" />
175-
<p> Sending Transaction </p>
176-
</div>
177-
)}
178-
179-
{isTransactionSent && (
180-
<div className="flex items-center gap-2 text-success-text">
181-
<CircleCheckIcon className="size-4" />
182-
<p> Transaction Sent </p>
183-
</div>
184-
)}
185-
186-
{props.status.type === "confirming" && (
187-
<div className="flex items-center gap-2 text-link-foreground">
188-
<Spinner className="size-4" />
189-
<p> Confirming Transaction </p>
190-
</div>
191-
)}
192-
193-
{props.status.type === "confirmed" && (
194-
<div className="flex items-center gap-2 text-success-text">
195-
<CircleCheckIcon className="size-4" />
196-
<p> Transaction Confirmed </p>
197-
</div>
198-
)}
199-
200-
{props.status.type === "failed" && (
201-
<div className="flex items-center gap-2 text-destructive-text">
202-
<CircleXIcon className="size-4" />
203-
<p> Transaction Failed </p>
204-
</div>
205-
)}
206-
</div>
207-
208-
{"txHash" in props.status && props.status.txHash && (
209-
<div className="flex justify-end gap-2.5 border-t p-4">
210-
{explorer && (
211-
<Button asChild variant="outline" size="sm" className="gap-2 ">
212-
<Link
213-
href={`${explorer}/tx/${props.status.txHash}`}
214-
target="_blank"
215-
>
216-
View on Explorer
217-
<ExternalLinkIcon className="size-3" />
218-
</Link>
219-
</Button>
220-
)}
221-
<CopyTextButton
222-
textToCopy={props.status.txHash}
223-
className=""
224-
textToShow="Transaction Hash"
225-
variant="outline"
226-
copyIconPosition="right"
227-
tooltip="Copy Transaction Hash"
228-
/>
229-
</div>
230-
)}
231-
</div>
232-
)}
233298
</div>
234299
);
235300
}

0 commit comments

Comments
 (0)