@@ -20,10 +20,12 @@ import {
2020 Settings ,
2121 ExternalLink
2222} from "lucide-react" ;
23- import { formatUnits , parseUnits , isAddress } from "viem" ;
23+ import { formatUnits , isAddress } from "viem" ;
2424import { formatAddress } from "@/lib/app/utils" ;
2525import { useStreamingData } from "@/hooks/jar/useStreamingData" ;
2626import { useStreamingActions } from "@/hooks/jar/useStreamingActions" ;
27+ import { useSuperfluidAccountInfo } from "@/hooks/jar/useSuperfluidAccountInfo" ;
28+ import { useSuperfluidTokenInfo } from "@/hooks/blockchain/useSuperfluidTokenInfo" ;
2729import { StreamProcessingCard } from "./StreamProcessingCard" ;
2830
2931
@@ -45,7 +47,7 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
4547 const [ newStreamToken , setNewStreamToken ] = useState ( "" ) ;
4648 const [ newStreamRate , setNewStreamRate ] = useState ( "" ) ;
4749
48- // Use real contract hooks
50+ // Use real Superfluid SDK hooks
4951 const {
5052 streams,
5153 // streamingConfig: config,
@@ -57,18 +59,23 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
5759
5860 const {
5961 createSuperStream,
60- isRegistering ,
61- isApproving ,
62- isProcessing ,
62+ isCreating ,
63+ updateSuperStream ,
64+ deleteSuperStream ,
6365 } = useStreamingActions ( jarAddress ) ;
6466
67+ const { data : accountInfo } = useSuperfluidAccountInfo ( jarAddress ) ;
68+
6569 const handleCreateSuperStream = async ( ) => {
66- await createSuperStream ( newStreamToken , newStreamRate ) ;
70+ try {
71+ await createSuperStream ( newStreamToken , newStreamRate ) ;
6772
68- // Reset form on success
69- setNewStreamSender ( "" ) ;
70- setNewStreamToken ( "" ) ;
71- setNewStreamRate ( "" ) ;
73+ // Reset form on success
74+ setNewStreamToken ( "" ) ;
75+ setNewStreamRate ( "" ) ;
76+ } catch ( error ) {
77+ console . error ( "Failed to create stream:" , error ) ;
78+ }
7279 } ;
7380
7481 if ( isLoadingStreams && ! streams . length ) {
@@ -93,13 +100,65 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
93100 </ CardHeader >
94101
95102 < CardContent >
96- < Tabs defaultValue = "active " className = "space-y-4" >
103+ < Tabs defaultValue = "overview " className = "space-y-4" >
97104 < TabsList className = "grid w-full grid-cols-3" >
105+ < TabsTrigger value = "overview" > Overview</ TabsTrigger >
98106 < TabsTrigger value = "active" > Active Streams</ TabsTrigger >
99107 { isAdmin && < TabsTrigger value = "manage" > Manage</ TabsTrigger > }
100- < TabsTrigger value = "config" > Configuration</ TabsTrigger >
101108 </ TabsList >
102109
110+ < TabsContent value = "overview" className = "space-y-4" >
111+ { accountInfo && (
112+ < Card >
113+ < CardHeader >
114+ < CardTitle className = "text-lg" > Jar Flow Overview</ CardTitle >
115+ </ CardHeader >
116+ < CardContent >
117+ < div className = "grid grid-cols-2 gap-4 text-sm" >
118+ < div >
119+ < span className = "text-gray-600" > Net Flow Rate:</ span >
120+ < div className = "font-mono text-green-600" >
121+ { accountInfo . formattedNetFlowRate } ETH/s
122+ </ div >
123+ </ div >
124+ < div >
125+ < span className = "text-gray-600" > Total Deposit:</ span >
126+ < div className = "font-mono text-blue-600" >
127+ { accountInfo . formattedTotalDeposit } ETH
128+ </ div >
129+ </ div >
130+ </ div >
131+ </ CardContent >
132+ </ Card >
133+ ) }
134+
135+ < Card >
136+ < CardHeader >
137+ < CardTitle className = "text-lg" > Stream Statistics</ CardTitle >
138+ </ CardHeader >
139+ < CardContent >
140+ < div className = "grid grid-cols-3 gap-4 text-center" >
141+ < div >
142+ < div className = "text-2xl font-bold text-blue-600" > { streams . length } </ div >
143+ < div className = "text-sm text-gray-600" > Active Streams</ div >
144+ </ div >
145+ < div >
146+ < div className = "text-2xl font-bold text-green-600" >
147+ { streams . reduce ( ( sum , stream ) => sum + Number ( formatUnits ( stream . ratePerSecond , 18 ) ) , 0 ) . toFixed ( 3 ) }
148+ </ div >
149+ < div className = "text-sm text-gray-600" > Total Rate (ETH/s)</ div >
150+ </ div >
151+ < div >
152+ < div className = "text-2xl font-bold text-purple-600" >
153+ { streams . reduce ( ( sum , stream ) => sum + Number ( formatUnits ( stream . totalStreamed , 18 ) ) , 0 ) . toFixed ( 3 ) }
154+ </ div >
155+ < div className = "text-sm text-gray-600" > Total Streamed (ETH)</ div >
156+ </ div >
157+ </ div >
158+ </ CardContent >
159+ </ Card >
160+ </ TabsContent >
161+
103162 < TabsContent value = "active" className = "space-y-4" >
104163 { streams . length === 0 ? (
105164 < div className = "text-center py-8 text-gray-500" >
@@ -108,78 +167,14 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
108167 </ div >
109168 ) : (
110169 streams . map ( ( stream ) => (
111- < Card key = { stream . id } className = "border-l-4 border-l-blue-500" >
112- < CardContent className = "p-4" >
113- < div className = "flex items-start justify-between" >
114- < div className = "space-y-2" >
115- < div className = "flex items-center gap-2" >
116- < Badge variant = { stream . isApproved ? "default" : "secondary" } >
117- { stream . isApproved ? "Active" : "Pending Approval" }
118- </ Badge >
119- < span className = "text-sm font-mono" >
120- Stream #{ stream . id }
121- </ span >
122- </ div >
123-
124- < div className = "text-sm space-y-1" >
125- < p >
126- < span className = "font-medium" > From:</ span > { " " }
127- < span className = "font-mono" > { formatAddress ( stream . sender ) } </ span >
128- </ p >
129- < p >
130- < span className = "font-medium" > Rate:</ span > { " " }
131- { formatStreamRate ( stream . ratePerSecond , 18 ) }
132- </ p >
133- < p >
134- < span className = "font-medium" > Total Streamed:</ span > { " " }
135- { formatUnits ( stream . totalStreamed , 18 ) }
136- </ p >
137- </ div >
138-
139- { stream . isApproved && (
140- < div className = "bg-green-50 p-2 rounded text-sm" >
141- < p className = "text-green-700 font-medium" >
142- Claimable: { formatUnits ( calculateClaimable ( stream ) , 18 ) } tokens
143- </ p >
144- </ div >
145- ) }
146- </ div >
147-
148- < div className = "flex gap-2" >
149- { /* {!stream.isApproved && isAdmin && (
150- <Button
151- size="sm"
152- onClick={() => handleApproveStream(stream.id)}
153- disabled={isApproving}
154- >
155- {isApproving ? (
156- <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-1"></div>
157- ) : (
158- <CheckCircle className="h-4 w-4 mr-1" />
159- )}
160- Approve
161- </Button>
162- )} */ }
163- { /*
164- {stream.isApproved && (
165- <Button
166- size="sm"
167- variant="outline"
168- onClick={() => handleProcessStream(stream.id)}
169- disabled={isProcessing}
170- >
171- {isProcessing ? (
172- <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-400 mr-1"></div>
173- ) : (
174- <Play className="h-4 w-4 mr-1" />
175- )}
176- Process
177- </Button>
178- )} */ }
179- </ div >
180- </ div >
181- </ CardContent >
182- </ Card >
170+ < StreamCard
171+ key = { stream . id }
172+ stream = { stream }
173+ onUpdate = { ( rate ) => updateSuperStream ( stream . token , rate ) }
174+ onDelete = { ( ) => deleteSuperStream ( stream . token ) }
175+ isUpdating = { isUpdating }
176+ isDeleting = { isDeleting }
177+ />
183178 ) )
184179 ) }
185180 </ TabsContent >
@@ -215,10 +210,10 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
215210
216211 < Button
217212 onClick = { handleCreateSuperStream }
218- disabled = { isRegistering }
213+ disabled = { isCreating }
219214 className = "w-full"
220215 >
221- { isRegistering ? (
216+ { isCreating ? (
222217 < div className = "animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" > </ div >
223218 ) : (
224219 < Settings className = "h-4 w-4 mr-2" />
@@ -234,3 +229,98 @@ export const StreamingPanel: React.FC<StreamingPanelProps> = ({
234229 </ Card >
235230 ) ;
236231} ;
232+
233+ interface StreamCardProps {
234+ stream : any ; // Using any for now, should be properly typed
235+ onUpdate : ( rate : string ) => void ;
236+ onDelete : ( ) => void ;
237+ isUpdating : boolean ;
238+ isDeleting : boolean ;
239+ }
240+
241+ const StreamCard : React . FC < StreamCardProps > = ( {
242+ stream,
243+ onUpdate,
244+ onDelete,
245+ isUpdating,
246+ isDeleting
247+ } ) => {
248+ const { data : tokenInfo } = useSuperfluidTokenInfo ( stream . token ) ;
249+
250+ return (
251+ < Card className = "border-l-4 border-l-blue-500" >
252+ < CardContent className = "p-4" >
253+ < div className = "flex items-start justify-between" >
254+ < div className = "space-y-2 flex-1" >
255+ < div className = "flex items-center gap-2" >
256+ < Badge variant = { stream . isActive ? "default" : "secondary" } >
257+ { stream . isActive ? "Active" : "Inactive" }
258+ </ Badge >
259+ < span className = "text-sm font-mono text-gray-600" >
260+ { stream . tokenSymbol || "TOKEN" }
261+ </ span >
262+ </ div >
263+
264+ < div className = "text-sm space-y-1" >
265+ < p >
266+ < span className = "font-medium" > From:</ span > { " " }
267+ < span className = "font-mono" > { formatAddress ( stream . sender ) } </ span >
268+ </ p >
269+ < p >
270+ < span className = "font-medium" > Rate:</ span > { " " }
271+ { formatStreamRate ( stream . ratePerSecond , 18 ) }
272+ </ p >
273+ < p >
274+ < span className = "font-medium" > Total Streamed:</ span > { " " }
275+ { formatUnits ( stream . totalStreamed , 18 ) }
276+ </ p >
277+ </ div >
278+
279+ { stream . isActive && (
280+ < div className = "bg-green-50 p-2 rounded text-sm" >
281+ < p className = "text-green-700 font-medium" >
282+ Claimable: { formatUnits ( calculateClaimable ( stream ) , 18 ) } tokens
283+ </ p >
284+ </ div >
285+ ) }
286+ </ div >
287+
288+ < div className = "flex gap-2 ml-4" >
289+ { isAdmin && (
290+ < >
291+ < Button
292+ size = "sm"
293+ variant = "outline"
294+ onClick = { ( ) => {
295+ const newRate = prompt ( "Enter new flow rate (wei per second):" , stream . ratePerSecond . toString ( ) ) ;
296+ if ( newRate ) onUpdate ( newRate ) ;
297+ } }
298+ disabled = { isUpdating }
299+ >
300+ { isUpdating ? (
301+ < div className = "animate-spin rounded-full h-4 w-4 border-b-2 border-gray-400" > </ div >
302+ ) : (
303+ < Settings className = "h-4 w-4" />
304+ ) }
305+ </ Button >
306+ < Button
307+ size = "sm"
308+ variant = "outline"
309+ onClick = { onDelete }
310+ disabled = { isDeleting }
311+ className = "text-red-600 hover:text-red-700"
312+ >
313+ { isDeleting ? (
314+ < div className = "animate-spin rounded-full h-4 w-4 border-b-2 border-red-400" > </ div >
315+ ) : (
316+ < Pause className = "h-4 w-4" />
317+ ) }
318+ </ Button >
319+ </ >
320+ ) }
321+ </ div >
322+ </ div >
323+ </ CardContent >
324+ </ Card >
325+ ) ;
326+ } ;
0 commit comments