Skip to content
Open
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,14 @@ function FundInvoiceButton() {

The provider rehydrates from storage **after mount** (SSR-safe). `disconnect()` clears storage immediately. See [WALLET_INTEGRATION_CONTRACT.md](WALLET_INTEGRATION_CONTRACT.md) for the full integration contract.

**Funding-intent flow:** On the invoice detail page (`/invest/[id]`), the Fund button is wallet-gated:
- **Disconnected**: clicking the Fund button calls `connect()` to prompt wallet connection before funding.
- **Connecting / No wallet**: the button is disabled to prevent duplicate connection attempts.
- **Connected**: the Fund button is enabled and the partial-funding form (`FundAmountInput`) can be submitted.
- **Pending (in-flight)**: while a funding transaction is in progress, both the Fund button and the amount input are disabled and the button shows "Funding…" with `aria-busy="true"`.

The wallet hook is consumed via `useWallet()` from `@/components/WalletProvider` (the canonical provider source), destructuring only `{ state, connect }` since `FundActions` does not need `walletData` or `disconnect`.

### NavMenu

`components/NavMenu.jsx` — Responsive site-wide header navigation used on every page.
Expand Down
4 changes: 2 additions & 2 deletions app/invest/[id]/FundActions.announce.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jest.mock("@/components/ToastProvider", () => ({
useToast: () => mockToast,
}));

jest.mock("@/components/WalletContext", () => ({
jest.mock("@/components/WalletProvider", () => ({
WALLET_STATES: {
DISCONNECTED: "disconnected",
CONNECTING: "connecting",
Expand All @@ -48,7 +48,7 @@ jest.mock("@/app/invest/MarketplaceContext", () => ({
}));

import FundActions from "./FundActions";
import { useWallet, WALLET_STATES } from "@/components/WalletContext";
import { useWallet, WALLET_STATES } from "@/components/WalletProvider";
import { copy } from "@/app/copy/en";

const mockUseWallet = useWallet as jest.MockedFunction<typeof useWallet>;
Expand Down
8 changes: 7 additions & 1 deletion app/invest/[id]/FundActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@

import { useCallback, useEffect, useRef, useState } from "react";
import { useToast } from "@/components/ToastProvider";
import { useWallet, WALLET_STATES } from "@/components/WalletContext";
// Canonical wallet hook — imports from the provider source of truth,
// not the deprecated WalletContext shim.
import { useWallet, WALLET_STATES } from "@/components/WalletProvider";
import FundAmountInput from "@/components/FundAmountInput";
import { useMarketplace } from "@/app/invest/MarketplaceContext";
import { copy } from "@/app/copy/en";
Expand Down Expand Up @@ -102,6 +104,10 @@ export async function copyInvoiceUrl(id) {
* Defaults to a mock that resolves immediately (placeholder until Stellar lands).
*/
export default function FundActions({ id, status, maxAmount, currency, yieldValue, performFund }) {
// Wallet gating: destructure only the canonical shape
// { state, walletData, connect, disconnect } from the consolidated provider.
// `state` is aliased to `walletState` for clarity in the Fund button
// gating logic below.
const { state: walletState, connect } = useWallet();
const toast = useToast();
const [isCopying, setIsCopying] = useState(false);
Expand Down
4 changes: 2 additions & 2 deletions app/invest/[id]/FundActions.optimistic.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jest.mock("@/components/ToastProvider", () => ({
useToast: () => mockToast,
}));

jest.mock("@/components/WalletContext", () => ({
jest.mock("@/components/WalletProvider", () => ({
WALLET_STATES: {
DISCONNECTED: "disconnected",
CONNECTING: "connecting",
Expand Down Expand Up @@ -73,7 +73,7 @@ jest.mock("@/app/invest/MarketplaceContext", () => ({
useMarketplace: jest.fn(),
}));

import { useWallet, WALLET_STATES } from "@/components/WalletContext";
import { useWallet, WALLET_STATES } from "@/components/WalletProvider";
import { useMarketplace } from "@/app/invest/MarketplaceContext";
import FundActions from "./FundActions";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ jest.mock("@/components/ToastProvider", () => ({
useToast: () => ({ success: jest.fn(), error: jest.fn(), info: jest.fn() }),
}));

jest.mock("@/components/WalletContext", () => ({
jest.mock("@/components/WalletProvider", () => ({
WALLET_STATES: {
DISCONNECTED: "disconnected",
CONNECTING: "connecting",
Expand Down
2 changes: 1 addition & 1 deletion app/invest/[id]/detail.a11y.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jest.mock("next/navigation", () => {
};
});

jest.mock("@/components/WalletContext", () => {
jest.mock("@/components/WalletProvider", () => {
return {
WALLET_STATES: {
DISCONNECTED: "disconnected",
Expand Down
4 changes: 2 additions & 2 deletions app/invest/[id]/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jest.mock("@/components/ToastProvider", () => ({
useToast: () => mockToast,
}));

jest.mock("@/components/WalletContext", () => ({
jest.mock("@/components/WalletProvider", () => ({
WALLET_STATES: {
DISCONNECTED: "disconnected",
CONNECTING: "connecting",
Expand Down Expand Up @@ -152,7 +152,7 @@ jest.mock("../lib", () => ({

import { notFound } from "next/navigation";
import { getInvoiceById } from "../lib";
import { useWallet, WALLET_STATES } from "@/components/WalletContext";
import { useWallet, WALLET_STATES } from "@/components/WalletProvider";
import { useOptimisticFund, FUNDING_STATES } from "@/lib/hooks/useOptimisticFund";
import InvoiceDetailPage from "./page";
import FundActions, { copyInvoiceUrl, copyToClipboardFallback } from "./FundActions";
Expand Down
Loading