From 3642b21ba1f809eb95db8b2002a00f18c6e8602a Mon Sep 17 00:00:00 2001 From: Ogunmodede Joel Taiwo Date: Mon, 29 Jun 2026 23:22:00 +0100 Subject: [PATCH 1/7] #623 [Frontend] StreamCreationWizard.startPolling hits wrong URL and wrong shape --- .../stream-creation/StreamCreationWizard.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/stream-creation/StreamCreationWizard.tsx b/frontend/src/components/stream-creation/StreamCreationWizard.tsx index 1d620e9..c5b3b96 100644 --- a/frontend/src/components/stream-creation/StreamCreationWizard.tsx +++ b/frontend/src/components/stream-creation/StreamCreationWizard.tsx @@ -13,6 +13,7 @@ import { fetchTokenBalanceDisplay } from "@/lib/soroban"; import { isValidStellarPublicKey } from "@/lib/stellar"; import toast from "react-hot-toast"; import { useRouter } from "next/navigation"; +import { getApiBaseUrl } from "@/lib/api/_shared"; export interface StreamFormData { recipient: string; @@ -346,18 +347,20 @@ export const StreamCreationWizard: React.FC = ({ const startTime = Date.now(); const TIMEOUT_MS = 30000; // 30 seconds const POLL_INTERVAL = 2000; // 2 seconds + const baseUrl = getApiBaseUrl(); while (Date.now() - startTime < TIMEOUT_MS) { try { - const response = await fetch(`/v1/streams?sender=${senderAddress}`); - const streams = await response.json(); + const response = await fetch(`${baseUrl}/v1/streams?sender=${senderAddress}`); + const payload = await response.json(); + const streams = Array.isArray(payload) ? payload : (payload.data ?? []); // Assuming the latest stream is what we want if (streams && streams.length > 0) { // Found! const newStream = streams[0]; // Simplification toast.success("Stream indexed and confirmed!"); - router.push(`/app/streams/${newStream.streamId}`); // Updated path to match new structure + router.push(`/streams/${newStream.streamId}`); // Updated path to match new structure return; } } catch (e) { @@ -381,7 +384,7 @@ export const StreamCreationWizard: React.FC = ({ // Step 2: Start Polling for Indexer setIsPolling(true); - await startPolling(formData.recipient); + await startPolling(walletPublicKey || ""); } catch (error) { console.error("Failed to create stream:", error); From 4930efea06576d4875c6a935ff52d6dad344e60e Mon Sep 17 00:00:00 2001 From: Ogunmodede Joel Taiwo Date: Mon, 29 Jun 2026 23:31:56 +0100 Subject: [PATCH 2/7] fix: resolve parsing and lint errors in page and dashboard views --- frontend/src/app/streams/create/page.tsx | 259 ------------------ .../components/dashboard/dashboard-view.tsx | 5 +- 2 files changed, 2 insertions(+), 262 deletions(-) diff --git a/frontend/src/app/streams/create/page.tsx b/frontend/src/app/streams/create/page.tsx index f1c7cfd..66164f6 100644 --- a/frontend/src/app/streams/create/page.tsx +++ b/frontend/src/app/streams/create/page.tsx @@ -8,263 +8,4 @@ export const metadata: Metadata = { export default function CreateStreamPage() { return ; -import React, { useState } from "react"; -import { - createStream, - toBaseUnits, - toDurationSeconds, - getTokenAddress, - toSorobanErrorMessage, - TOKEN_ADDRESSES -} from "@/lib/soroban"; -import { hasValidPrecision, validateAmountInput } from "@/utils/amount"; -import { isValidStellarPublicKey } from "@/lib/stellar"; -import { toast } from "react-hot-toast"; -import { useRouter } from "next/navigation"; -import Link from "next/link"; -import { ArrowLeft } from "lucide-react"; -import { useWallet } from "@/context/wallet-context"; - -const TOKEN_DECIMALS = 7; - -export default function CreateStreamPage() { - const { status, session } = useWallet(); - const router = useRouter(); - const [nowTimestamp] = useState(() => Date.now()); - const [loading, setLoading] = useState(false); - const [txState, setTxState] = useState<"idle" | "signing" | "submitted" | "confirming">("idle"); - const [formData, setFormData] = useState({ - recipient: "", - token: "XLM", - amount: "", - duration: "30", // days - }); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - if (status !== "connected" || !session) { - toast.error("Please connect your wallet first."); - return; - } - - // Validate recipient - if (!formData.recipient.trim()) { - toast.error("Recipient address is required"); - return; - } - if (!isValidStellarPublicKey(formData.recipient)) { - toast.error("Invalid Stellar public key format"); - return; - } - - // Validate amount - const validationError = validateAmountInput(formData.amount, TOKEN_DECIMALS); - if (validationError) { - toast.error(validationError); - return; - } - - // Validate duration - const durationNum = parseFloat(formData.duration); - if (isNaN(durationNum) || durationNum <= 0) { - toast.error("Duration must be a positive number"); - return; - } - - setLoading(true); - setTxState("signing"); - - try { - const amountBigInt = toBaseUnits(formData.amount); - const durationBigInt = toDurationSeconds(formData.duration, "days"); - const tokenAddress = getTokenAddress(formData.token); - - const result = await createStream(session, { - recipient: formData.recipient, - tokenAddress, - amount: amountBigInt, - durationSeconds: durationBigInt, - }); - - if (result.success) { - setTxState("confirming"); - toast.success("Stream created successfully!"); - // Small delay to allow indexer to catch up - setTimeout(() => { - router.push("/dashboard"); - }, 2000); - } - } catch (error) { - console.error("Stream creation failed:", error); - toast.error(toSorobanErrorMessage(error)); - } finally { - setLoading(false); - setTxState("idle"); - } - }; - - const getButtonText = () => { - if (!loading) return "Start Streaming"; - switch (txState) { - case "signing": return "Confirm in Wallet..."; - case "submitted": return "Submitting to Network..."; - case "confirming": return "Finalizing Stream..."; - default: return "Processing..."; - } - }; - - // Inline validation feedback for the amount field. validateAmountInput - // returns an error message when invalid and null when valid. Only show it - // once the user has typed something — the empty case is handled on submit. - const amountError = formData.amount - ? validateAmountInput(formData.amount, TOKEN_DECIMALS) - : null; - - const recipientError = formData.recipient - ? (!isValidStellarPublicKey(formData.recipient) ? "Invalid Stellar public key format" : null) - : null; - - const durationError = formData.duration - ? (isNaN(Number(formData.duration)) || Number(formData.duration) <= 0 - ? "Duration must be a positive number" - : null) - : null; - - return ( -
- - - Back to Dashboard - - -
-

Create New Stream

-

- Set up a real-time payment stream to any Stellar address. -

- -
-
- - setFormData({ ...formData, recipient: e.target.value })} - required - /> - {recipientError && ( -

{recipientError}

- )} -
- -
-
- - -
-
- - { - const newValue = e.target.value; - // Only allow valid number characters and check precision - if (newValue === '' || /^\d*\.?\d*$/.test(newValue)) { - if (hasValidPrecision(newValue, TOKEN_DECIMALS)) { - setFormData({ ...formData, amount: newValue }); - } - } - }} - required - /> - {amountError && ( -

{amountError}

- )} -
-
- -
- - setFormData({ ...formData, duration: e.target.value })} - required - /> - {durationError && ( -

{durationError}

- )} -
- -
-
- Streaming Rate - - {formData.amount && formData.duration && Number(formData.duration) > 0 - ? (Number(formData.amount) / (Number(formData.duration) * 86400)).toFixed(8) - : "0.00000000"} {formData.token}/sec - -
-
- Estimated End Date - - {formData.duration && Number(formData.duration) > 0 - ? new Date(nowTimestamp + Number(formData.duration) * 86400000).toLocaleDateString() - : "—"} - -
-
- - - - {status !== "connected" && ( -

- Please connect your wallet to create a stream. -

- )} -
-
-
- ); } diff --git a/frontend/src/components/dashboard/dashboard-view.tsx b/frontend/src/components/dashboard/dashboard-view.tsx index 9247431..d109bd4 100644 --- a/frontend/src/components/dashboard/dashboard-view.tsx +++ b/frontend/src/components/dashboard/dashboard-view.tsx @@ -109,13 +109,12 @@ const SIDEBAR_ITEMS: SidebarItem[] = [ function SkeletonCard({ className = "" }: { className?: string }) { return ( ); } From b22d7f33c793f4d2c37e3c54f67ac70fb8a9ca47 Mon Sep 17 00:00:00 2001 From: Ogunmodede Joel Taiwo Date: Mon, 29 Jun 2026 23:44:07 +0100 Subject: [PATCH 3/7] fix: resolve Skeleton type error in dashboard view --- frontend/src/components/dashboard/dashboard-view.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/dashboard/dashboard-view.tsx b/frontend/src/components/dashboard/dashboard-view.tsx index d109bd4..5dac949 100644 --- a/frontend/src/components/dashboard/dashboard-view.tsx +++ b/frontend/src/components/dashboard/dashboard-view.tsx @@ -15,7 +15,7 @@ import toast from "react-hot-toast"; * - Error state: "Failed to load streams" with a retry button */ -import { Skeleton } from "@/components/ui/Skeleton"; + import { getDashboardAnalytics, fetchDashboardData, @@ -108,13 +108,13 @@ const SIDEBAR_ITEMS: SidebarItem[] = [ /** Shimmer card used as a placeholder while data loads */ function SkeletonCard({ className = "" }: { className?: string }) { return ( -