From cadcf9de832c5e51b2bb00c3adb32236ff973f01 Mon Sep 17 00:00:00 2001 From: Chibuikem Michael Ilonze Date: Thu, 26 Feb 2026 11:07:21 +0100 Subject: [PATCH] feat(frontend): wire incoming withdraw to freighter soroban flow --- frontend/app/incoming/page.tsx | 7 ++++- frontend/components/IncomingStreams.tsx | 30 ++++++++----------- .../components/dashboard/dashboard-view.tsx | 29 +++++++++++++++++- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/frontend/app/incoming/page.tsx b/frontend/app/incoming/page.tsx index b2bdf52..5d0b643 100644 --- a/frontend/app/incoming/page.tsx +++ b/frontend/app/incoming/page.tsx @@ -43,7 +43,12 @@ export default function IncomingPage() {

Loading incoming streams...

) : ( - + { + // Withdraw action is currently handled in the dashboard context. + }} + /> )} diff --git a/frontend/components/IncomingStreams.tsx b/frontend/components/IncomingStreams.tsx index 603beae..55d4fc5 100644 --- a/frontend/components/IncomingStreams.tsx +++ b/frontend/components/IncomingStreams.tsx @@ -1,33 +1,25 @@ 'use client'; import React, { useState } from 'react'; -import toast from "react-hot-toast"; import type { Stream } from '@/lib/dashboard'; interface IncomingStreamsProps { streams: Stream[]; + onWithdraw: (stream: Stream) => Promise; + withdrawingStreamId?: string | null; } -const IncomingStreams: React.FC = ({ streams }) => { +const IncomingStreams: React.FC = ({ + streams, + onWithdraw, + withdrawingStreamId = null, +}) => { const [filter, setFilter] = useState<'All' | 'Active' | 'Completed' | 'Paused'>('All'); const filteredStreams = filter === 'All' ? streams : streams.filter(s => s.status === filter); - const handleWithdraw = async () => { - const toastId = toast.loading("Transaction pending..."); - - try { - // Simulate async transaction (replace with real blockchain call later) - await new Promise((resolve) => setTimeout(resolve, 2000)); - - toast.success("Withdrawal successful!", { id: toastId }); - } catch { - toast.error("Transaction failed.", { id: toastId }); - } - }; - const handleFilterChange = (e: React.ChangeEvent) => { setFilter(e.target.value as 'All' | 'Active' | 'Completed' | 'Paused'); }; @@ -84,14 +76,16 @@ const IncomingStreams: React.FC = ({ streams }) => { diff --git a/frontend/components/dashboard/dashboard-view.tsx b/frontend/components/dashboard/dashboard-view.tsx index e0e0082..2687ba8 100644 --- a/frontend/components/dashboard/dashboard-view.tsx +++ b/frontend/components/dashboard/dashboard-view.tsx @@ -28,6 +28,7 @@ import { createStream as sorobanCreateStream, topUpStream as sorobanTopUp, cancelStream as sorobanCancel, + withdrawFromStream as sorobanWithdraw, toBaseUnits, toDurationSeconds, getTokenAddress, @@ -316,6 +317,8 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) { >(null); const [streamFormMessage, setStreamFormMessage] = React.useState(null); + const [withdrawingIncomingStreamId, setWithdrawingIncomingStreamId] = + React.useState(null); // --- Snapshot State (merged) --- const [snapshot, setSnapshot] = React.useState(null); @@ -556,6 +559,26 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) { } }; + const handleIncomingWithdraw = async (stream: Stream) => { + const toastId = toast.loading("Withdrawing stream funds…"); + setWithdrawingIncomingStreamId(stream.id); + + try { + await sorobanWithdraw(session, { + streamId: BigInt(stream.id.replace(/\D/g, "") || "0"), + }); + + const refreshed = await fetchDashboardData(session.publicKey); + setSnapshot(refreshed); + toast.success("Withdrawal successful!", { id: toastId }); + } catch (err) { + toast.error(toSorobanErrorMessage(err), { id: toastId }); + throw err; + } finally { + setWithdrawingIncomingStreamId(null); + } + }; + const handleFormCreateStream = (event: React.FormEvent) => { event.preventDefault(); const hasRequiredFields = @@ -590,7 +613,11 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) { if (activeTab === "incoming") { return (
- +
); }