|
1 | 1 | "use client"; |
2 | 2 |
|
3 | | -import { useState, Suspense } from "react"; |
| 3 | +import { useState, useMemo, Suspense } from "react"; |
4 | 4 | import { useRouter } from "next/navigation"; |
5 | 5 | import dynamic from "next/dynamic"; |
6 | 6 | import { motion, AnimatePresence } from "framer-motion"; |
@@ -28,18 +28,30 @@ import { signWithFreighter } from "@/lib/stellar/freighter"; |
28 | 28 | import { apiClient } from "@/lib/api/axios"; |
29 | 29 | import { MULTI_CURRENCY_ASSETS, MOCK_RATES } from "@/lib/utils/constants"; |
30 | 30 | import { WalletModalFallback } from "@/components/wallet/WalletModalFallback"; |
| 31 | +import { WalletModalErrorBoundary } from "@/components/wallet/WalletModalErrorBoundary"; |
31 | 32 | import { QRCodeModal } from "@/components/payments/QRCode"; |
32 | | -const WalletModal = dynamic( |
33 | | - () => import("@/components/wallet/WalletModal").then((m) => m.WalletModal), |
34 | | - { ssr: false }, |
35 | | -); |
36 | 33 |
|
37 | 34 | export default function PaymentLinkPage() { |
38 | 35 | const router = useRouter(); |
39 | 36 | const { isConnected, connect, address } = useWalletStore(); |
40 | 37 | const { error: notifyError } = useNotify(); |
41 | 38 | const [walletModalOpen, setWalletModalOpen] = useState(false); |
42 | 39 | const [qrModalOpen, setQrModalOpen] = useState(false); |
| 40 | + // Bumping this recreates the `dynamic()` import below with a fresh promise, |
| 41 | + // so retrying after a chunk-load failure re-fetches the chunk instead of |
| 42 | + // replaying the same cached rejection. |
| 43 | + const [walletModalRetryKey, setWalletModalRetryKey] = useState(0); |
| 44 | + const WalletModal = useMemo( |
| 45 | + () => |
| 46 | + dynamic( |
| 47 | + () => import("@/components/wallet/WalletModal").then((m) => m.WalletModal), |
| 48 | + { ssr: false }, |
| 49 | + ), |
| 50 | + // walletModalRetryKey isn't read inside the factory — it's only a cache |
| 51 | + // key so bumping it forces a new dynamic() call (and a fresh import()). |
| 52 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 53 | + [walletModalRetryKey], |
| 54 | + ); |
43 | 55 |
|
44 | 56 | // Mock data for this link |
45 | 57 | const linkData = { |
@@ -158,19 +170,25 @@ export default function PaymentLinkPage() { |
158 | 170 | return ( |
159 | 171 | <div className="min-h-screen bg-background flex flex-col items-center justify-center p-4"> |
160 | 172 | <div className="w-full max-w-md"> |
161 | | - <Suspense |
162 | | - fallback={ |
163 | | - <WalletModalFallback |
| 173 | + <WalletModalErrorBoundary |
| 174 | + open={walletModalOpen} |
| 175 | + onOpenChange={setWalletModalOpen} |
| 176 | + onRetry={() => setWalletModalRetryKey((key) => key + 1)} |
| 177 | + > |
| 178 | + <Suspense |
| 179 | + fallback={ |
| 180 | + <WalletModalFallback |
| 181 | + open={walletModalOpen} |
| 182 | + onOpenChange={setWalletModalOpen} |
| 183 | + /> |
| 184 | + } |
| 185 | + > |
| 186 | + <WalletModal |
164 | 187 | open={walletModalOpen} |
165 | 188 | onOpenChange={setWalletModalOpen} |
166 | 189 | /> |
167 | | - } |
168 | | - > |
169 | | - <WalletModal |
170 | | - open={walletModalOpen} |
171 | | - onOpenChange={setWalletModalOpen} |
172 | | - /> |
173 | | - </Suspense> |
| 190 | + </Suspense> |
| 191 | + </WalletModalErrorBoundary> |
174 | 192 |
|
175 | 193 | {/* Merchant Branding Header */} |
176 | 194 | <div className="flex flex-col items-center mb-8"> |
|
0 commit comments