11"use client" ;
22
3- import { memo } from 'react' ;
3+ import { memo , useRef } from 'react' ;
4+ import { useVirtualizer } from '@tanstack/react-virtual' ;
45import { Card , CardContent , CardHeader , CardTitle , CardDescription } from '@/components/ui' ;
56import { Button } from '@/components/ui' ;
67import { EmptyState } from '@/components/shared' ;
7- import { ArrowUpRight , ArrowDownLeft , Inbox , RefreshCcw , ExternalLink } from 'lucide-react' ;
8+ import { ArrowUpRight , ArrowDownLeft , Inbox , RefreshCcw , ExternalLink , Loader2 } from 'lucide-react' ;
89import { getStellarExplorerTxUrl } from '@/lib/utils/explorer' ;
10+ import { useTransactionHistory } from '@/lib/hooks/useTransactionHistory' ;
911
1012interface WalletTx {
1113 id : string ;
1214 type : 'receive' | 'send' ;
1315 label : string ;
1416 amount : number ;
15- time : string ;
16- txHash ?: string ;
17+ assetCode : string ;
18+ timestamp : string ;
19+ txHash : string ;
20+ counterparty : string ;
1721}
1822
19- const mockTxHistory : WalletTx [ ] = [
20- { id : 'w1' , type : 'receive' , label : 'Payment from link_02' , amount : 45.5 , time : '2h ago' , txHash : '0x1234567890abcdef1234567890abcdef12345678' } ,
21- { id : 'w2' , type : 'receive' , label : 'Payment from link_01' , amount : 750 , time : '5h ago' , txHash : '0xabcdef1234567890abcdef1234567890abcdef12' } ,
22- { id : 'w3' , type : 'send' , label : 'Settlement to GTBank' , amount : 1200 , time : 'Yesterday' } ,
23- { id : 'w4' , type : 'receive' , label : 'Payment from link_03' , amount : 29 , time : 'Yesterday' , txHash : '0x9999999999abcdef1234567890abcdef12345678' } ,
24- ] ;
25-
2623const WalletActivityItem = memo ( function WalletActivityItem ( { tx } : { tx : WalletTx } ) {
2724 return (
2825 < div className = "flex items-center gap-3 py-2.5 px-2 rounded-xl hover:bg-muted transition-colors" >
@@ -35,10 +32,10 @@ const WalletActivityItem = memo(function WalletActivityItem({ tx }: { tx: Wallet
3532 </ div >
3633 < div className = "flex-1 min-w-0" >
3734 < p className = "text-sm font-medium text-foreground" > { tx . label } </ p >
38- < p className = "text-xs text-muted-foreground" > { tx . time } </ p >
35+ < p className = "text-xs text-muted-foreground" > { tx . timestamp } </ p >
3936 </ div >
4037 < span className = { `text-sm font-semibold ${ tx . type === 'receive' ? 'text-emerald-600' : 'text-foreground' } ` } >
41- { tx . type === 'receive' ? '+' : '-' } { tx . amount . toFixed ( 2 ) } USDC
38+ { tx . type === 'receive' ? '+' : '-' } { tx . amount . toFixed ( 2 ) } { tx . assetCode }
4239 </ span >
4340 { tx . txHash && (
4441 < a
@@ -57,25 +54,83 @@ const WalletActivityItem = memo(function WalletActivityItem({ tx }: { tx: Wallet
5754} ) ;
5855
5956export function WalletActivityHistory ( ) {
57+ const { transactions, loading, error, refetch } = useTransactionHistory ( ) ;
58+ const parentRef = useRef < HTMLDivElement > ( null ) ;
59+
60+ const virtualizer = useVirtualizer ( {
61+ count : transactions . length ,
62+ getScrollElement : ( ) => parentRef . current ,
63+ estimateSize : ( ) => 60 ,
64+ overscan : 5 ,
65+ } ) ;
66+
6067 return (
6168 < Card className = "border border-border bg-card shadow-sm" >
6269 < CardHeader className = "flex flex-row items-center justify-between" >
6370 < div >
6471 < CardTitle className = "text-base font-semibold text-foreground" > Wallet Activity</ CardTitle >
6572 < CardDescription > Recent on-chain transactions</ CardDescription >
6673 </ div >
67- < Button variant = "ghost" aria-label = "Refresh balances" className = "text-xs text-muted-foreground min-h-[44px] px-3 rounded-lg" >
68- < RefreshCcw className = "w-3 h-3 mr-1.5" /> Refresh
74+ < Button
75+ variant = "ghost"
76+ aria-label = "Refresh transactions"
77+ onClick = { refetch }
78+ disabled = { loading }
79+ className = "text-xs text-muted-foreground min-h-[44px] px-3 rounded-lg"
80+ >
81+ { loading ? (
82+ < Loader2 className = "w-3 h-3 mr-1.5 animate-spin" />
83+ ) : (
84+ < RefreshCcw className = "w-3 h-3 mr-1.5" />
85+ ) } { ' ' }
86+ Refresh
6987 </ Button >
7088 </ CardHeader >
7189 < CardContent >
72- { mockTxHistory . length === 0 ? (
73- < EmptyState icon = { Inbox } title = "No wallet activity yet" description = "On-chain transactions will appear here once your wallet receives payments." />
90+ { loading && transactions . length === 0 ? (
91+ < div className = "flex items-center justify-center py-8" >
92+ < Loader2 className = "w-5 h-5 animate-spin text-muted-foreground" />
93+ </ div >
94+ ) : error ? (
95+ < EmptyState
96+ icon = { Inbox }
97+ title = "Failed to load transactions"
98+ description = { error }
99+ />
100+ ) : transactions . length === 0 ? (
101+ < EmptyState
102+ icon = { Inbox }
103+ title = "No wallet activity yet"
104+ description = "On-chain transactions will appear here once your wallet receives payments."
105+ />
74106 ) : (
75- < div className = "space-y-2" >
76- { mockTxHistory . map ( ( tx ) => (
77- < WalletActivityItem key = { tx . id } tx = { tx } />
78- ) ) }
107+ < div
108+ ref = { parentRef }
109+ className = "h-[300px] overflow-auto"
110+ >
111+ < div
112+ style = { {
113+ height : `${ virtualizer . getTotalSize ( ) } px` ,
114+ width : '100%' ,
115+ position : 'relative' ,
116+ } }
117+ >
118+ { virtualizer . getVirtualItems ( ) . map ( ( virtualRow ) => (
119+ < div
120+ key = { virtualRow . key }
121+ style = { {
122+ position : 'absolute' ,
123+ top : 0 ,
124+ left : 0 ,
125+ width : '100%' ,
126+ height : `${ virtualRow . size } px` ,
127+ transform : `translateY(${ virtualRow . start } px)` ,
128+ } }
129+ >
130+ < WalletActivityItem tx = { transactions [ virtualRow . index ] } />
131+ </ div >
132+ ) ) }
133+ </ div >
79134 </ div >
80135 ) }
81136 </ CardContent >
0 commit comments