22
33import React , { useState } from 'react' ;
44import type { Stream } from '@/lib/dashboard' ;
5+ import { useStreamingAmount } from '@/hooks/useStreamingAmount' ;
56
67interface IncomingStreamsProps {
78 streams : Stream [ ] ;
89 onWithdraw : ( stream : Stream ) => Promise < void > ;
910 withdrawingStreamId ?: string | null ;
1011}
1112
13+ function formatTokenAmount ( value : number ) : string {
14+ if ( ! Number . isFinite ( value ) ) return '0.0000' ;
15+
16+ return new Intl . NumberFormat ( 'en-US' , {
17+ minimumFractionDigits : 4 ,
18+ maximumFractionDigits : 4 ,
19+ } ) . format ( value ) ;
20+ }
21+
22+ const ClaimableAmount : React . FC < { stream : Stream } > = ( { stream } ) => {
23+ const claimable = useStreamingAmount ( {
24+ deposited : stream . deposited ,
25+ withdrawn : stream . withdrawn ,
26+ ratePerSecond : stream . ratePerSecond ,
27+ lastUpdateTime : stream . lastUpdateTime ,
28+ isActive : stream . status === 'Active' && stream . isActive ,
29+ } ) ;
30+
31+ const liveRate = stream . status === 'Active' && stream . ratePerSecond > 0 ;
32+
33+ return (
34+ < div className = "flex flex-col" >
35+ < span className = { `font-bold tabular-nums ${ liveRate ? 'text-emerald-600 dark:text-emerald-300' : 'text-gray-900 dark:text-gray-100' } ` } >
36+ { formatTokenAmount ( claimable ) } { stream . token }
37+ </ span >
38+ < span className = { `text-xs tabular-nums ${ liveRate ? 'text-emerald-500 dark:text-emerald-400' : 'text-gray-400 dark:text-gray-500' } ` } >
39+ { liveRate
40+ ? `+${ formatTokenAmount ( stream . ratePerSecond ) } ${ stream . token } /sec`
41+ : 'Stream inactive' }
42+ </ span >
43+ </ div >
44+ ) ;
45+ } ;
46+
1247const IncomingStreams : React . FC < IncomingStreamsProps > = ( {
1348 streams,
1449 onWithdraw,
@@ -18,7 +53,7 @@ const IncomingStreams: React.FC<IncomingStreamsProps> = ({
1853
1954 const filteredStreams = filter === 'All'
2055 ? streams
21- : streams . filter ( s => s . status === filter ) ;
56+ : streams . filter ( ( s ) => s . status === filter ) ;
2257
2358 const handleFilterChange = ( e : React . ChangeEvent < HTMLSelectElement > ) => {
2459 setFilter ( e . target . value as 'All' | 'Active' | 'Completed' | 'Paused' ) ;
@@ -29,7 +64,9 @@ const IncomingStreams: React.FC<IncomingStreamsProps> = ({
2964 < div className = "p-6 border-b border-white/20 dark:border-white/10 flex flex-col md:flex-row md:items-center justify-between gap-4" >
3065 < div >
3166 < h2 className = "text-xl font-bold text-gray-900 dark:text-white" > Incoming Payment Streams</ h2 >
32- < p className = "text-sm text-gray-500 dark:text-gray-400 mt-1" > Manage and withdraw from your active incoming streams</ p >
67+ < p className = "text-sm text-gray-500 dark:text-gray-400 mt-1" >
68+ Manage and withdraw from your active incoming streams
69+ </ p >
3370 </ div >
3471 < div className = "flex items-center gap-2" >
3572 < label htmlFor = "filter" className = "text-sm font-medium text-gray-700 dark:text-gray-300" > Filter:</ label >
@@ -55,17 +92,24 @@ const IncomingStreams: React.FC<IncomingStreamsProps> = ({
5592 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Token</ th >
5693 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Deposited</ th >
5794 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Withdrawn</ th >
95+ < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Claimable</ th >
5896 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Status</ th >
5997 < th className = "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" > Actions</ th >
6098 </ tr >
6199 </ thead >
62100 < tbody className = "bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700" >
63101 { filteredStreams . map ( ( stream ) => (
64102 < tr key = { stream . id } className = "hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors" >
65- < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 font-mono" > { stream . id } </ td >
103+ < td className = "px-6 py-4 whitespace-nowrap" >
104+ < div className = "text-sm text-gray-900 dark:text-gray-100 font-mono" > { stream . recipient } </ div >
105+ < div className = "text-xs text-gray-500 dark:text-gray-400" > Stream #{ stream . id } </ div >
106+ </ td >
66107 < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100" > { stream . token } </ td >
67- < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100" > { stream . deposited } { stream . token } </ td >
68- < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 font-bold" > { stream . withdrawn } { stream . token } </ td >
108+ < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 tabular-nums" > { formatTokenAmount ( stream . deposited ) } { stream . token } </ td >
109+ < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 font-bold tabular-nums" > { formatTokenAmount ( stream . withdrawn ) } { stream . token } </ td >
110+ < td className = "px-6 py-4 whitespace-nowrap text-sm" >
111+ < ClaimableAmount stream = { stream } />
112+ </ td >
69113 < td className = "px-6 py-4 whitespace-nowrap text-sm" >
70114 < span className = { `px-2 inline-flex text-xs leading-5 font-semibold rounded-full
71115 ${ stream . status === 'Active' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' :
0 commit comments