Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 46 additions & 37 deletions frontend/components/dashboard/dashboard-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,17 +239,17 @@
onShowDetails(stream);
}}
>
<td>{stream.date}</td>
<td>
<code className="text-xs">{stream.recipient}</code>
</td>
<td className="font-semibold text-accent">
{stream.deposited} {stream.token}
</td>
<td className="text-slate-400">
{stream.withdrawn} {stream.token}
</td>
<td className="text-right">
<td>{stream.date}</td>
<td>
<code className="text-xs">{stream.recipient}</code>
</td>
<td className="font-semibold text-accent">
{stream.deposited} {stream.token}
</td>
<td className="text-slate-400">
{stream.withdrawn} {stream.token}
</td>
<td className="text-right">
<div className="flex items-center justify-end gap-2">
{/^\d+$/.test(stream.id) ? (
<Link
Expand All @@ -259,13 +259,13 @@
Details
</Link>
) : null}
<button
type="button"
className="secondary-button py-1 px-3 text-sm h-auto"
<button
type="button"
className="secondary-button py-1 px-3 text-sm h-auto"
onClick={() => onTopUp(stream)}
>
Add Funds
</button>
>
Add Funds
</button>
<button
type="button"
className="py-1 px-3 text-sm rounded-full border border-red-500/40 text-red-400 hover:bg-red-500/10 transition-colors font-semibold"
Expand All @@ -274,9 +274,9 @@
Cancel
</button>
</div>
</td>
</tr>
))}
</td>
</tr>
))}
</tbody>
</table>
</div>
Expand Down Expand Up @@ -432,6 +432,15 @@
setStreamFormMessage(null);
};

const handleTopUp = (streamId: string) => {

Check warning on line 435 in frontend/components/dashboard/dashboard-view.tsx

View workflow job for this annotation

GitHub Actions / Frontend CI

'handleTopUp' is assigned a value but never used
const amount = prompt(`Enter amount to add to stream ${streamId}:`);
if (amount && !Number.isNaN(parseFloat(amount)) && parseFloat(amount) > 0) {
console.log(`Adding ${amount} funds to stream ${streamId}`);
// TODO: Integrate with Soroban contract's top_up_stream function
alert(`Successfully added ${amount} to stream ${streamId}`);
}
};

const handleApplyTemplate = (templateId: string) => {
const template = templates.find((item) => item.id === templateId);
if (!template) return;
Expand Down Expand Up @@ -503,8 +512,8 @@
setTemplates((prev) => prev.filter((item) => item.id !== templateId));
if (selectedTemplateId === templateId) setSelectedTemplateId(null);
if (editingTemplateId === templateId) {
setEditingTemplateId(null);
setTemplateNameInput("");
setEditingTemplateId(null);
setTemplateNameInput("");
}
};

Expand Down Expand Up @@ -688,7 +697,7 @@
(endDate.getTime() - startDate.getTime()) / 1000,
);
if (durationSeconds <= 0) {
setStreamFormMessage({
setStreamFormMessage({
text: "End time must be after start time.",
tone: "error",
});
Expand All @@ -708,8 +717,8 @@
handleResetStreamForm();
setStreamFormMessage({
text: "Stream submitted to wallet and confirmed on-chain.",
tone: "success",
});
tone: "success",
});
} catch (err) {
setStreamFormMessage({
text: toSorobanErrorMessage(err),
Expand Down Expand Up @@ -996,18 +1005,18 @@
</div>

<div className="stream-form__row">
<label>
Cadence (seconds)
<input
type="number"
min="1"
step="1"
value={streamForm.cadenceSeconds}
onChange={(event) =>
updateStreamForm("cadenceSeconds", event.target.value)
}
/>
</label>
<label>
Cadence (seconds)
<input
type="number"
min="1"
step="1"
value={streamForm.cadenceSeconds}
onChange={(event) =>
updateStreamForm("cadenceSeconds", event.target.value)
}
/>
</label>
</div>

<label>
Expand Down
9 changes: 8 additions & 1 deletion frontend/components/wallet/WalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import React, { useState, useRef, useEffect } from "react";
import { useWallet } from "@/context/wallet-context";
import {
formatNetwork,
shortenPublicKey,
isExpectedNetwork,
} from "@/lib/wallet";
Expand Down Expand Up @@ -62,6 +61,14 @@ export function WalletButton() {
disconnect();
};

// Close modal when connection is successful
useEffect(() => {
if (status === "connected") {
// eslint-disable-next-line react-hooks/set-state-in-effect
setModalOpen(false);
}
}, [status]);

// Don't render anything until client-side hydration is complete to avoid
// localStorage mismatch flicker.
if (!isHydrated) {
Expand Down
9 changes: 6 additions & 3 deletions frontend/components/wallet/WalletModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* - Dismiss via Escape key or backdrop click.
*/

import React, { useEffect, useCallback, useState } from "react";
import React, { useEffect, useCallback } from "react";
import { type WalletId } from "@/lib/wallet";
import { useWallet } from "@/context/wallet-context";

Expand All @@ -22,7 +22,10 @@ interface WalletModalProps {
onClose: () => void;
}

const WALLET_NOTES: Partial<Record<WalletId, string>> = {};
const WALLET_NOTES: Partial<Record<WalletId, string>> = {
albedo: "A popup window will open for authentication.",
xbull: "Available as browser extension or mobile app.",
};

export function WalletModal({ onClose }: WalletModalProps) {
const { wallets, status, selectedWalletId, errorMessage, connect, clearError } =
Expand Down Expand Up @@ -193,7 +196,7 @@ export function WalletModal({ onClose }: WalletModalProps) {
>
{isConnecting
? "Waiting for wallet approval…"
: "Freighter"}
: "Supported wallets: Freighter, Albedo, xBull"}
</p>
</div>
</div>
Expand Down
87 changes: 15 additions & 72 deletions frontend/components/wallet/wallet-entry.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
"use client";

import { DashboardView } from "@/components/dashboard/dashboard-view";
import { WalletModal } from "@/components/wallet/WalletModal";
import { useWallet } from "@/context/wallet-context";
import { useState, useEffect } from "react";

export function WalletEntry() {
const {
wallets,
status,
session,
selectedWalletId,
errorMessage,
isHydrated,
connect,
disconnect,
clearError,
} = useWallet();
const { status, session, isHydrated, disconnect } = useWallet();
const [showModal, setShowModal] = useState(false);

useEffect(() => {
if (!isHydrated) return;

// Update modal visibility based on connection status
// eslint-disable-next-line react-hooks/set-state-in-effect
setShowModal(status !== "connected");
}, [isHydrated, status]);

if (!isHydrated) {
return (
Expand All @@ -34,65 +35,7 @@ export function WalletEntry() {
return <DashboardView session={session} onDisconnect={disconnect} />;
}

const isConnecting = status === "connecting";

return (
<main className="app-shell">
<section className="wallet-panel">
<p className="kicker">FlowFi Entry</p>
<h1>Select a wallet to continue</h1>
<p className="subtitle">
Choose your preferred wallet provider. The connection session is
stored locally so you stay signed in after refresh.
</p>

{errorMessage ? (
<div className="wallet-error" role="alert">
<span>{errorMessage}</span>
<button type="button" className="inline-link" onClick={clearError}>
Dismiss
</button>
</div>
) : null}

<div className="wallet-grid">
{wallets.map((wallet, index) => {
const isActiveWallet = selectedWalletId === wallet.id;
const isConnectingThisWallet = isConnecting && isActiveWallet;

return (
<article
key={wallet.id}
className="wallet-card"
data-active={isActiveWallet ? "true" : undefined}
style={{ animationDelay: `${index * 110}ms` }}
>
<header className="wallet-card__header">
<h2>{wallet.name}</h2>
<span>{wallet.badge}</span>
</header>
<p>{wallet.description}</p>
<button
type="button"
className="wallet-button"
disabled={isConnecting}
onClick={() => void connect(wallet.id)}
>
{isConnectingThisWallet
? "Connecting..."
: `Connect ${wallet.name}`}
</button>
</article>
);
})}
</div>

<p className="wallet-status" data-busy={isConnecting ? "true" : undefined}>
{isConnecting
? "Awaiting wallet approval..."
: "Supported wallets: Freighter, Albedo, xBull."}
</p>
</section>
</main>
);
return showModal ? (
<WalletModal onClose={() => setShowModal(false)} />
) : null;
}
2 changes: 1 addition & 1 deletion frontend/context/wallet-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface WalletContextValue {
// so stale persisted sessions are discarded rather than causing type errors.
const STORAGE_KEY = "flowfi.wallet.session.v1";
const WalletContext = createContext<WalletContextValue | undefined>(undefined);
const VALID_WALLET_IDS: WalletId[] = ["freighter"];
const VALID_WALLET_IDS: WalletId[] = ["freighter", "albedo", "xbull"];

interface WalletState {
status: WalletStatus;
Expand Down
Loading
Loading