Skip to content

Commit fbd8d10

Browse files
committed
feat: add Albedo and xBull wallet support with mock connections
- Added Albedo and xBull to SUPPORTED_WALLETS array - Implemented mock connection functions for Albedo and xBull wallets - Updated WalletModal to display all three wallets (Freighter, Albedo, xBull) - Added wallet-specific notes in modal (Albedo popup, xBull extension/mobile) - Updated footer to show all supported wallets - Fixed TypeScript errors in dashboard-view and dashboard.ts - Auto-close modal on successful connection
1 parent c0af847 commit fbd8d10

7 files changed

Lines changed: 94 additions & 28 deletions

File tree

frontend/components/dashboard/dashboard-view.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,6 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) {
434434
}
435435
};
436436

437-
const handleCreateStream = async (data: StreamFormData) => {
438-
console.log("Creating stream with data:", data);
439-
// TODO: Integrate with Soroban contract's create_stream function
440-
await new Promise((resolve) => setTimeout(resolve, 1500));
441-
alert(
442-
`Stream created successfully!\n\nRecipient: ${data.recipient}\nToken: ${data.token}\nAmount: ${data.amount}\nDuration: ${data.duration} ${data.durationUnit}`,
443-
);
444-
setShowWizard(false);
445437
const handleApplyTemplate = (templateId: string) => {
446438
const template = templates.find((item) => item.id === templateId);
447439
if (!template) {
@@ -597,16 +589,11 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) {
597589
if (activeTab === "incoming") {
598590
return (
599591
<div className="mt-8">
600-
<IncomingStreams />
592+
<IncomingStreams streams={stats?.incomingStreams || []} />
601593
</div>
602594
);
603595
}
604596

605-
if (activeTab === "overview") {
606-
if (!stats) {
607-
return <div className="mt-8"><IncomingStreams streams={stats?.incomingStreams || []} /></div>;
608-
}
609-
610597
if (activeTab === "streams") {
611598
return (
612599
<div className="dashboard-content-stack mt-8">

frontend/components/wallet/WalletButton.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export function WalletButton() {
6262
disconnect();
6363
};
6464

65+
// Close modal when connection is successful
66+
useEffect(() => {
67+
if (status === "connected") {
68+
setModalOpen(false);
69+
}
70+
}, [status]);
71+
6572
// Don't render anything until client-side hydration is complete to avoid
6673
// localStorage mismatch flicker.
6774
if (!isHydrated) {

frontend/components/wallet/WalletModal.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ interface WalletModalProps {
2222
onClose: () => void;
2323
}
2424

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

2730
export function WalletModal({ onClose }: WalletModalProps) {
2831
const { wallets, status, selectedWalletId, errorMessage, connect, clearError } =
@@ -193,7 +196,7 @@ export function WalletModal({ onClose }: WalletModalProps) {
193196
>
194197
{isConnecting
195198
? "Waiting for wallet approval…"
196-
: "Freighter"}
199+
: "Supported wallets: Freighter, Albedo, xBull"}
197200
</p>
198201
</div>
199202
</div>

frontend/components/wallet/wallet-entry.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
"use client";
22

33
import { DashboardView } from "@/components/dashboard/dashboard-view";
4-
import { WalletSelectionModal } from "@/components/wallet/wallet-selection-modal";
4+
import { WalletModal } from "@/components/wallet/WalletModal";
55
import { useWallet } from "@/context/wallet-context";
6-
import { useEffect } from "react";
6+
import { useState, useEffect } from "react";
77

88
export function WalletEntry() {
99
const { status, session, isHydrated, disconnect } = useWallet();
10+
const [showModal, setShowModal] = useState(false);
11+
12+
useEffect(() => {
13+
if (isHydrated && status !== "connected") {
14+
setShowModal(true);
15+
} else if (status === "connected") {
16+
setShowModal(false);
17+
}
18+
}, [isHydrated, status]);
1019

1120
if (!isHydrated) {
1221
return (
@@ -26,10 +35,7 @@ export function WalletEntry() {
2635
return <DashboardView session={session} onDisconnect={disconnect} />;
2736
}
2837

29-
return (
30-
<WalletSelectionModal
31-
isOpen={status !== "connected"}
32-
onClose={undefined}
33-
/>
34-
);
38+
return showModal ? (
39+
<WalletModal onClose={() => setShowModal(false)} />
40+
) : null;
3541
}

frontend/context/wallet-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ interface WalletContextValue {
3535
// so stale persisted sessions are discarded rather than causing type errors.
3636
const STORAGE_KEY = "flowfi.wallet.session.v1";
3737
const WalletContext = createContext<WalletContextValue | undefined>(undefined);
38-
const VALID_WALLET_IDS: WalletId[] = ["freighter"];
38+
const VALID_WALLET_IDS: WalletId[] = ["freighter", "albedo", "xbull"];
3939

4040
interface WalletState {
4141
status: WalletStatus;

frontend/lib/dashboard.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { BackendStream } from "./api-types";
2+
import type { WalletId } from "./wallet";
23

34
export interface ActivityItem {
45
id: string;
@@ -45,7 +46,7 @@ const MOCK_STATS_BY_WALLET: Record<WalletId, DashboardSnapshot | null> = {
4546
totalReceived: 4720,
4647
totalValueLocked: 32140,
4748
activeStreamsCount: 2,
48-
streams: [
49+
outgoingStreams: [
4950
{
5051
id: "stream-1",
5152
date: "2023-10-25",
@@ -67,6 +68,7 @@ const MOCK_STATS_BY_WALLET: Record<WalletId, DashboardSnapshot | null> = {
6768
withdrawn: 300,
6869
},
6970
],
71+
incomingStreams: [],
7072
recentActivity: [
7173
{
7274
id: "act-1",
@@ -94,6 +96,8 @@ const MOCK_STATS_BY_WALLET: Record<WalletId, DashboardSnapshot | null> = {
9496
},
9597
],
9698
},
99+
albedo: null,
100+
xbull: null,
97101
};
98102
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001/v1";
99103

frontend/lib/wallet.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
getNetworkDetails,
2929
} from "@stellar/freighter-api";
3030

31-
export type WalletId = "freighter";
31+
export type WalletId = "freighter" | "albedo" | "xbull";
3232

3333
export interface WalletDescriptor {
3434
id: WalletId;
@@ -71,6 +71,18 @@ export const SUPPORTED_WALLETS: readonly WalletDescriptor[] = [
7171
badge: "Extension",
7272
description: "Direct browser wallet for Stellar accounts and Soroban apps.",
7373
},
74+
{
75+
id: "albedo",
76+
name: "Albedo",
77+
badge: "Web",
78+
description: "Connect via web authentication popup. No extension required.",
79+
},
80+
{
81+
id: "xbull",
82+
name: "xBull",
83+
badge: "Extension",
84+
description: "Browser extension and mobile wallet for Stellar ecosystem.",
85+
},
7486
];
7587

7688
// ── Internal helpers ──────────────────────────────────────────────────────────
@@ -79,6 +91,7 @@ function buildSession(
7991
walletId: WalletId,
8092
publicKey: string,
8193
network: string,
94+
mocked: boolean = false,
8295
): WalletSession {
8396
const descriptor = SUPPORTED_WALLETS.find((w) => w.id === walletId);
8497

@@ -92,7 +105,7 @@ function buildSession(
92105
publicKey,
93106
connectedAt: new Date().toISOString(),
94107
network,
95-
mocked: false,
108+
mocked,
96109
};
97110
}
98111

@@ -133,6 +146,48 @@ async function connectFreighter(): Promise<WalletSession> {
133146
return buildSession("freighter", address, networkId);
134147
}
135148

149+
// ── Albedo (Mock) ──────────────────────────────────────────────────────────────
150+
151+
/**
152+
* Mock connection for Albedo wallet.
153+
* Simulates a connection with a delay to show loading state.
154+
* In production, this would integrate with Albedo's web auth popup.
155+
*/
156+
async function connectAlbedo(): Promise<WalletSession> {
157+
// Simulate connection delay
158+
await new Promise((resolve) => setTimeout(resolve, 1500));
159+
160+
// Generate a mock public key for demonstration
161+
// In production, this would come from Albedo's authentication flow
162+
// Stellar public keys are base32 encoded and 56 characters long, starting with G
163+
const mockPublicKey = "G" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".substring(0, 55);
164+
165+
const networkId = STELLAR_NETWORK === "MAINNET" ? "Mainnet" : "Testnet";
166+
167+
return buildSession("albedo", mockPublicKey, networkId, true);
168+
}
169+
170+
// ── xBull (Mock) ────────────────────────────────────────────────────────────────
171+
172+
/**
173+
* Mock connection for xBull wallet.
174+
* Simulates a connection with a delay to show loading state.
175+
* In production, this would integrate with xBull extension or mobile handoff.
176+
*/
177+
async function connectXBull(): Promise<WalletSession> {
178+
// Simulate connection delay
179+
await new Promise((resolve) => setTimeout(resolve, 1500));
180+
181+
// Generate a mock public key for demonstration
182+
// In production, this would come from xBull's connection flow
183+
// Stellar public keys are base32 encoded and 56 characters long, starting with G
184+
const mockPublicKey = "G" + "ZYXWVUTSRQPONMLKJIHGFEDCBA234567".substring(0, 55);
185+
186+
const networkId = STELLAR_NETWORK === "MAINNET" ? "Mainnet" : "Testnet";
187+
188+
return buildSession("xbull", mockPublicKey, networkId, true);
189+
}
190+
136191
// ── Public connect dispatch ───────────────────────────────────────────────────
137192

138193
export async function connectWallet(
@@ -141,6 +196,10 @@ export async function connectWallet(
141196
switch (walletId) {
142197
case "freighter":
143198
return connectFreighter();
199+
case "albedo":
200+
return connectAlbedo();
201+
case "xbull":
202+
return connectXBull();
144203
default:
145204
throw new Error("Unsupported wallet selected.");
146205
}

0 commit comments

Comments
 (0)