Skip to content

Commit 2951738

Browse files
committed
update recent transfers table
1 parent aabe00e commit 2951738

File tree

2 files changed

+142
-68
lines changed

2 files changed

+142
-68
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import { CheckIcon, CopyIcon, XIcon } from "lucide-react";
2+
import { CheckIcon, CircleSlashIcon, CopyIcon, XIcon } from "lucide-react";
33
import { useMemo } from "react";
44
import { isAddress, type ThirdwebClient, ZERO_ADDRESS } from "thirdweb";
55
import { Blobbie, type SocialProfile, useSocialProfiles } from "thirdweb/react";
@@ -61,9 +61,16 @@ export function WalletAddress(props: {
6161
// special case for zero address
6262
if (address === ZERO_ADDRESS) {
6363
return (
64-
<span className={cn("cursor-pointer font-mono", props.className)}>
65-
{shortenedAddress}
66-
</span>
64+
<div className="flex items-center gap-2 py-2">
65+
<CircleSlashIcon
66+
className={cn("size-6 text-muted-foreground/70", props.iconClassName)}
67+
/>
68+
<span
69+
className={cn("cursor-pointer font-mono text-sm", props.className)}
70+
>
71+
{shortenedAddress}
72+
</span>
73+
</div>
6774
);
6875
}
6976

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/RecentTransfers.tsx

Lines changed: 131 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"use client";
22
import { formatDistanceToNow } from "date-fns";
33
import {
4+
ArrowRightIcon,
45
ChevronLeftIcon,
56
ChevronRightIcon,
67
ExternalLinkIcon,
78
} from "lucide-react";
8-
import { useState } from "react";
9+
import { useMemo, useState } from "react";
910
import { type ThirdwebClient, type ThirdwebContract, toTokens } from "thirdweb";
1011
import type { ChainMetadata } from "thirdweb/chains";
1112
import { WalletAddress } from "@/components/blocks/wallet-address";
@@ -20,7 +21,6 @@ import {
2021
TableHeader,
2122
TableRow,
2223
} from "@/components/ui/table";
23-
import { cn } from "@/lib/utils";
2424
import {
2525
type TokenTransfersData,
2626
useTokenTransfers,
@@ -45,6 +45,32 @@ function RecentTransfersUI(props: {
4545
explorerUrl: string;
4646
client: ThirdwebClient;
4747
}) {
48+
const groupedData = useMemo(() => {
49+
const data: Array<{
50+
group: TokenTransfersData[];
51+
transactionHash: string;
52+
blockTimestamp: string;
53+
}> = [];
54+
55+
for (const transfer of props.data) {
56+
const existingGroup = data.find(
57+
(group) => group.transactionHash === transfer.transaction_hash,
58+
);
59+
60+
if (existingGroup) {
61+
existingGroup.group.push(transfer);
62+
} else {
63+
data.push({
64+
group: [transfer],
65+
transactionHash: transfer.transaction_hash,
66+
blockTimestamp: transfer.block_timestamp,
67+
});
68+
}
69+
}
70+
71+
return data;
72+
}, [props.data]);
73+
4874
return (
4975
<div>
5076
<div className="p-4 lg:p-6 bg-card border rounded-b-none border-b-0 rounded-lg">
@@ -74,76 +100,89 @@ function RecentTransfersUI(props: {
74100
// biome-ignore lint/suspicious/noArrayIndexKey: EXPECTED
75101
<SkeletonRow key={index} />
76102
))
77-
: props.data.map((transfer) => (
103+
: groupedData.map((group) => (
78104
<TableRow
79105
className="fade-in-0 animate-in duration-300"
80-
key={
81-
transfer.transaction_hash +
82-
transfer.amount +
83-
transfer.block_number +
84-
transfer.from_address
85-
}
106+
key={group.transactionHash}
86107
>
87-
<TableCell className="text-sm">
88-
<WalletAddress
89-
address={transfer.from_address}
90-
client={props.client}
91-
/>
92-
</TableCell>
93-
<TableCell className="text-sm">
94-
<WalletAddress
95-
address={transfer.to_address}
96-
client={props.client}
97-
/>
108+
{/* From */}
109+
<TableCell className="relative space-y-1">
110+
{group.group.map((transfer) => (
111+
<div
112+
className="h-10 flex items-center gap-6 w-[150px]"
113+
key={transfer.log_index}
114+
>
115+
<WalletAddress
116+
address={transfer.from_address}
117+
client={props.client}
118+
iconClassName="size-4.5"
119+
/>
120+
<ArrowRightIcon className="size-4 text-muted-foreground/50 absolute -right-1 lg:right-3" />
121+
</div>
122+
))}
98123
</TableCell>
99-
<TableCell className="text-sm ">
100-
<div className="flex items-center gap-1.5">
101-
<span>
102-
{tokenAmountFormatter.format(
103-
Number(
104-
toTokens(
105-
BigInt(transfer.amount),
106-
props.tokenMetadata.decimals,
107-
),
108-
),
109-
)}
110-
</span>
111-
<span className="text-muted-foreground text-xs">
112-
{props.tokenMetadata.symbol}
113-
</span>
114-
</div>
124+
125+
{/* To */}
126+
<TableCell className="relative space-y-1">
127+
{group.group.map((transfer) => (
128+
<div
129+
className="h-10 flex items-center gap-6 w-[150px]"
130+
key={transfer.log_index}
131+
>
132+
<WalletAddress
133+
address={transfer.to_address}
134+
client={props.client}
135+
key={transfer.log_index}
136+
iconClassName="size-4.5"
137+
/>
138+
</div>
139+
))}
115140
</TableCell>
116-
<TableCell className="text-sm">
117-
{formatDistanceToNow(
118-
new Date(
119-
transfer.block_timestamp.endsWith("Z")
120-
? transfer.block_timestamp
121-
: `${transfer.block_timestamp}Z`,
122-
),
123-
{
124-
addSuffix: true,
125-
},
126-
)}
141+
142+
{/* Amount */}
143+
<TableCell className="space-y-1">
144+
{group.group.map((transfer) => (
145+
<div
146+
className="h-10 flex items-center"
147+
key={transfer.log_index}
148+
>
149+
<TokenAmount
150+
amount={transfer.amount}
151+
decimals={props.tokenMetadata.decimals}
152+
symbol={props.tokenMetadata.symbol}
153+
/>
154+
</div>
155+
))}
127156
</TableCell>
157+
158+
{/* timestamp */}
128159
<TableCell>
129-
<Button
130-
asChild
131-
className="h-8 w-8 p-0"
132-
size="sm"
133-
variant="ghost"
160+
<div
161+
key={group.blockTimestamp}
162+
className="capitalize text-muted-foreground text-sm"
134163
>
135-
<a
136-
className={cn(
137-
"flex items-center justify-center",
138-
"hover:bg-accent hover:text-accent-foreground",
139-
)}
140-
href={`${props.explorerUrl}/tx/${transfer.transaction_hash}`}
141-
rel="noopener noreferrer"
142-
target="_blank"
164+
{timestamp(group.blockTimestamp)}
165+
</div>
166+
</TableCell>
167+
168+
{/* transaction */}
169+
<TableCell>
170+
<div className="flex items-center justify-center">
171+
<Button
172+
asChild
173+
size="sm"
174+
variant="outline"
175+
className="text-muted-foreground hover:text-foreground rounded-full size-9 p-0 flex items-center justify-center"
143176
>
144-
<ExternalLinkIcon className="h-4 w-4" />
145-
</a>
146-
</Button>
177+
<a
178+
href={`${props.explorerUrl}/tx/${group.transactionHash}`}
179+
rel="noopener noreferrer"
180+
target="_blank"
181+
>
182+
<ExternalLinkIcon className="size-3.5" />
183+
</a>
184+
</Button>
185+
</div>
147186
</TableCell>
148187
</TableRow>
149188
))}
@@ -183,6 +222,34 @@ function RecentTransfersUI(props: {
183222
);
184223
}
185224

225+
function timestamp(block_timestamp: string) {
226+
return formatDistanceToNow(
227+
new Date(
228+
block_timestamp.endsWith("Z") ? block_timestamp : `${block_timestamp}Z`,
229+
),
230+
{
231+
addSuffix: true,
232+
},
233+
);
234+
}
235+
236+
function TokenAmount(props: {
237+
amount: string;
238+
decimals: number;
239+
symbol: string;
240+
}) {
241+
return (
242+
<div className="flex items-center gap-1.5">
243+
<span>
244+
{tokenAmountFormatter.format(
245+
Number(toTokens(BigInt(props.amount), props.decimals)),
246+
)}
247+
</span>
248+
<span className="text-muted-foreground text-xs">{props.symbol}</span>
249+
</div>
250+
);
251+
}
252+
186253
function SkeletonRow() {
187254
return (
188255
<TableRow className="fade-in-0 h-[73px] animate-in duration-300">
@@ -199,7 +266,7 @@ function SkeletonRow() {
199266
<Skeleton className="h-6 w-32" />
200267
</TableCell>
201268
<TableCell>
202-
<Skeleton className="h-6 w-6" />
269+
<Skeleton className="size-9 rounded-full" />
203270
</TableCell>
204271
</TableRow>
205272
);

0 commit comments

Comments
 (0)