diff --git a/FREIGHTER_MODAL_README.md b/FREIGHTER_MODAL_README.md new file mode 100644 index 0000000..4f202db --- /dev/null +++ b/FREIGHTER_MODAL_README.md @@ -0,0 +1,99 @@ +# Freighter Wallet Connection Modal + +## Overview +This document describes the dedicated Freighter wallet connection modal implemented for TradeFlow-Web as part of the wallet connection enhancement. + +## Features Implemented + +### ✅ Acceptance Criteria Met + +1. **Modal opens when user clicks "Connect Wallet" button** + - The modal is triggered from the main page header button + - Uses proper state management with `isModalOpen` state + +2. **Freighter browser extension detection** + - Automatically detects if Freighter is installed using `window.freighter` object + - Shows loading state while checking for installation + - TypeScript declarations added in `src/types/freighter.d.ts` + +3. **Clear UI state when Freighter is NOT installed** + - Shows amber warning banner when Freighter is not detected + - Provides direct download link to official Freighter extension + - Includes educational information about Freighter wallet + - External link indicator for better UX + +4. **Successful connection handling** + - Retrieves user's Stellar public key using existing Web3 store + - Stores public key in global state via `useWalletConnection` hook + - Shows success toast notification upon connection + +5. **UI updates for authenticated state** + - Header button displays truncated public key format: `GABC...XYZ1` + - Button shows "Connecting..." during connection process + - Pulse animation removed when wallet is connected + - Button disabled during connection to prevent multiple clicks + +## Technical Implementation + +### Components +- **`FreighterConnectModal.tsx`**: Main modal component with wallet detection +- **`page.tsx`**: Updated to use new modal and Web3 store integration +- **`freighter.d.ts`**: TypeScript declarations for Freighter API + +### State Management +- Uses existing `useWeb3Store` for wallet connection state +- Integrates with `useWalletConnection` hook for clean separation +- Proper loading and error state handling + +### UI/UX Features +- Loading spinner during wallet detection +- Clear success/error states with appropriate colors +- Responsive design with proper mobile support +- Smooth transitions and micro-interactions +- Accessible modal with proper close functionality + +## Usage + +### For Users +1. Click "Connect Wallet" button in header +2. If Freighter is not installed, click "Download Freighter Extension" +3. Install and refresh the page +4. Click "Connect Wallet" again +5. Approve connection in Freighter extension +6. Wallet address will appear in header button + +### For Developers +```tsx +import FreighterConnectModal from '../components/FreighterConnectModal'; + +const [isModalOpen, setIsModalOpen] = useState(false); + + setIsModalOpen(false)} +/> +``` + +## Testing Checklist + +- [x] Modal opens and closes properly +- [x] Freighter detection works (with/without extension) +- [x] Download link opens in new tab +- [x] Connection flow integrates with Web3 store +- [x] Header button shows correct states +- [x] Error handling displays properly +- [x] TypeScript compilation without errors +- [x] Responsive design on mobile devices + +## Dependencies +- React hooks (`useState`, `useEffect`) +- Lucide React icons +- React Hot Toast for notifications +- Existing Web3 store infrastructure +- Tailwind CSS for styling + +## Future Enhancements +- Support for additional wallet types +- Connection persistence across sessions +- Network switching capabilities +- Advanced transaction signing flows diff --git a/src/app/page.tsx b/src/app/page.tsx index 54d9bb8..6ad7e52 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,7 @@ import SkeletonRow from "../components/SkeletonRow"; import Navbar from "../components/Navbar"; import StickyHeader from "../components/StickyHeader"; import Card from "../components/Card"; -import WalletModal from "../components/WalletModal"; +import FreighterConnectModal from "../components/FreighterConnectModal"; import InvoiceMintForm from "../components/InvoiceMintForm"; import InvoiceTable from "../components/InvoiceTable"; import InvoiceFilter, { InvoiceFilters } from "../components/InvoiceFilter"; @@ -24,13 +24,14 @@ import StarIcon from "../components/StarIcon"; import { api } from "../lib/api"; import type { InvoiceSummary } from "../../types/api"; import { RiskSocketClient } from "../lib/riskSocket"; +import { useWalletConnection } from "../stores/useWeb3Store"; import { showError, showSuccess } from "../lib/toast"; import Icon from "../components/ui/Icon"; export default function Page() { const router = useRouter(); const searchParams = useSearchParams(); - const [address, setAddress] = useState(""); + const { isConnected, walletAddress, isConnecting } = useWalletConnection(); const [invoices, setInvoices] = useState([]); const [loading, setLoading] = useState(false); const [showMintForm, setShowMintForm] = useState(false); @@ -58,6 +59,7 @@ export default function Page() { router.replace(newUrl); }, [filters, router]); + // 1. Connect Stellar Wallet (supports Freighter, Albedo, xBull) const handleConnectWallet = async (walletType: WalletType) => { try { @@ -99,7 +101,7 @@ export default function Page() { }, []); useEffect(() => { - if (!address) { + if (!walletAddress) { riskSocketRef.current?.disconnect(); riskSocketRef.current = null; return; @@ -125,14 +127,14 @@ export default function Page() { riskSocketRef.current = null; } }; - }, [address]); + }, [walletAddress]); useEffect(() => { - if (!address) return; + if (!walletAddress) return; if (invoices.length === 0) return; riskSocketRef.current?.syncInvoices(invoices.map((i) => i.id)); - }, [address, invoices]); + }, [walletAddress, invoices]); const toast = useTransactionToast(); const handleTestToast = () => { @@ -169,8 +171,17 @@ export default function Page() {
+ setIsModalOpen(false)} + /> setIsModalOpen(false)} diff --git a/src/components/FreighterConnectModal.tsx b/src/components/FreighterConnectModal.tsx new file mode 100644 index 0000000..b1a6f87 --- /dev/null +++ b/src/components/FreighterConnectModal.tsx @@ -0,0 +1,212 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { Wallet, Download, Shield, ExternalLink, Loader2 } from "lucide-react"; +import { useWalletConnection } from "../stores/useWeb3Store"; +import { FREIGHTER_ID } from "../lib/stellar"; +import toast from "react-hot-toast"; + +interface FreighterConnectModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function FreighterConnectModal({ isOpen, onClose }: FreighterConnectModalProps) { + const { connectWallet, isConnecting, error } = useWalletConnection(); + const [isFreighterInstalled, setIsFreighterInstalled] = useState(null); + const [isChecking, setIsChecking] = useState(true); + + // Check if Freighter is installed + useEffect(() => { + const checkFreighterInstallation = async () => { + if (!isOpen) return; + + setIsChecking(true); + + try { + // Check if Freighter is available in window + const isInstalled = typeof window !== 'undefined' && + !!window.freighter; + + setIsFreighterInstalled(isInstalled); + } catch (error) { + console.error('Error checking Freighter installation:', error); + setIsFreighterInstalled(false); + } finally { + setIsChecking(false); + } + }; + + checkFreighterInstallation(); + }, [isOpen]); + + // Handle wallet connection + const handleConnect = async () => { + try { + await connectWallet(FREIGHTER_ID); + toast.success("Wallet connected successfully!"); + onClose(); + } catch (error: any) { + console.error('Connection failed:', error); + toast.error(error.message || "Failed to connect to Freighter wallet"); + } + }; + + // Handle download Freighter + const handleDownloadFreighter = () => { + window.open('https://www.freighter.app/', '_blank'); + }; + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+
+
+ +
+
+

Connect Freighter Wallet

+

Secure Stellar wallet connection

+
+
+ +
+ + {/* Content */} +
+ {isChecking ? ( +
+ +

Checking for Freighter wallet...

+
+ ) : isFreighterInstalled ? ( +
+ {/* Freighter detected state */} +
+
+
+ +
+
+

Freighter Detected

+

Your wallet is ready to connect

+
+
+
+ + {/* Connect button */} + + + {/* Error display */} + {error && ( +
+

{error}

+
+ )} +
+ ) : ( +
+ {/* Freighter not detected state */} +
+
+
+ +
+
+

Freighter Not Detected

+

Install the browser extension to continue

+
+
+
+ + {/* Download button */} + + + {/* Additional info */} +
+

About Freighter

+
    +
  • + + Secure browser extension wallet for Stellar +
  • +
  • + + Supports multiple accounts and advanced features +
  • +
  • + + Trusted by thousands of Stellar users +
  • +
+
+ + {/* External link indicator */} +
+ + Opens in new tab +
+
+ )} + + {/* Terms */} +
+

+ By connecting your wallet, you agree to the{' '} + + Terms of Service + {' '} + and{' '} + + Privacy Policy + +

+
+
+
+
+ ); +} diff --git a/src/types/freighter.d.ts b/src/types/freighter.d.ts new file mode 100644 index 0000000..c49a95a --- /dev/null +++ b/src/types/freighter.d.ts @@ -0,0 +1,13 @@ +declare global { + interface Window { + freighter?: { + isConnected(): Promise; + getAddress(): Promise<{ address: string }>; + signTransaction(xdr: string, options?: any): Promise<{ signedTxXdr: string }>; + getNetwork(): Promise<{ network: string }>; + disconnect(): Promise; + }; + } +} + +export {};