@b3dotfun/sdk 0.0.7-alpha.16 → 0.0.7-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +28 -25
  2. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +26 -7
  3. package/dist/cjs/anyspend/react/components/modals/EnterRecipientModal.js +5 -5
  4. package/dist/cjs/global-account/react/hooks/useAccountWallet.js +4 -4
  5. package/dist/cjs/global-account/react/hooks/useTokenFromUrl.js +4 -4
  6. package/dist/cjs/shared/react/hooks/index.d.ts +1 -0
  7. package/dist/cjs/shared/react/hooks/index.js +17 -0
  8. package/dist/cjs/shared/react/hooks/useNavigation.d.ts +17 -0
  9. package/dist/cjs/shared/react/hooks/useNavigation.js +57 -0
  10. package/dist/cjs/shared/react/index.d.ts +1 -0
  11. package/dist/cjs/shared/react/index.js +17 -0
  12. package/dist/esm/anyspend/react/components/AnySpendCustom.js +29 -26
  13. package/dist/esm/anyspend/react/components/common/OrderDetails.js +24 -5
  14. package/dist/esm/anyspend/react/components/modals/EnterRecipientModal.js +1 -1
  15. package/dist/esm/global-account/react/hooks/useAccountWallet.js +4 -4
  16. package/dist/esm/global-account/react/hooks/useTokenFromUrl.js +3 -3
  17. package/dist/esm/shared/react/hooks/index.d.ts +1 -0
  18. package/dist/esm/shared/react/hooks/index.js +1 -0
  19. package/dist/esm/shared/react/hooks/useNavigation.d.ts +17 -0
  20. package/dist/esm/shared/react/hooks/useNavigation.js +52 -0
  21. package/dist/esm/shared/react/index.d.ts +1 -0
  22. package/dist/esm/shared/react/index.js +1 -0
  23. package/dist/types/shared/react/hooks/index.d.ts +1 -0
  24. package/dist/types/shared/react/hooks/useNavigation.d.ts +17 -0
  25. package/dist/types/shared/react/index.d.ts +1 -0
  26. package/package.json +1 -1
  27. package/src/anyspend/react/components/AnySpendCustom.tsx +44 -9
  28. package/src/anyspend/react/components/common/OrderDetails.tsx +30 -7
  29. package/src/anyspend/react/components/modals/EnterRecipientModal.tsx +1 -1
  30. package/src/global-account/react/hooks/useAccountWallet.tsx +4 -4
  31. package/src/global-account/react/hooks/useTokenFromUrl.tsx +3 -3
  32. package/src/shared/react/hooks/index.ts +1 -0
  33. package/src/shared/react/hooks/useNavigation.ts +61 -0
  34. package/src/shared/react/index.ts +1 -0
@@ -95,14 +95,15 @@ function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", recipient
95
95
  const [activeTab, setActiveTab] = (0, react_2.useState)("crypto");
96
96
  // Get current user's wallet
97
97
  const currentWallet = (0, react_1.useAccountWallet)();
98
- const recipientPropsProfile = (0, react_1.useBsmntProfile)({ address: recipientAddressProps });
99
- const recipientAddress = recipientAddressProps || currentWallet.address;
100
- const recipientEnsName = recipientAddressProps
101
- ? recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "")
102
- : currentWallet.ensName;
103
- const recipientImageUrl = recipientAddressProps
104
- ? recipientPropsProfile.data?.avatar
105
- : currentWallet.wallet.meta?.icon;
98
+ // Add state for recipient modal
99
+ const [isRecipientModalOpen, setIsRecipientModalOpen] = (0, react_2.useState)(false);
100
+ // Add state for custom recipient
101
+ const [customRecipientAddress, setCustomRecipientAddress] = (0, react_2.useState)(recipientAddressProps);
102
+ // Update recipient logic to use custom recipient
103
+ const recipientAddress = customRecipientAddress || currentWallet.address;
104
+ const recipientPropsProfile = (0, react_1.useBsmntProfile)({ address: recipientAddress });
105
+ const recipientEnsName = recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "");
106
+ const recipientImageUrl = recipientPropsProfile.data?.avatar || currentWallet.wallet.meta?.icon;
106
107
  const [orderId, setOrderId] = (0, react_2.useState)(loadOrder);
107
108
  const [srcChainId, setSrcChainId] = (0, react_2.useState)(isMainnet ? chains_1.base.id : chains_1.baseSepolia.id);
108
109
  // Get token list for token balance check
@@ -350,7 +351,7 @@ function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", recipient
350
351
  ? "Receive NFT at"
351
352
  : orderType === anyspend_1.OrderType.JoinTournament
352
353
  ? "Join for"
353
- : "Recipient" }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full justify-between border-none p-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [recipientImageUrl && ((0, jsx_runtime_1.jsx)("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-1", children: [recipientEnsName && (0, jsx_runtime_1.jsxs)("span", { children: ["@", recipientEnsName] }), (0, jsx_runtime_1.jsx)("span", { children: (0, centerTruncate_1.default)(recipientAddress) })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRightCircle, { className: "ml-2 size-4 shrink-0 opacity-50" })] }) })] })) : null;
354
+ : "Recipient" }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full justify-between border-none p-0", onClick: () => setIsRecipientModalOpen(true), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [recipientImageUrl && ((0, jsx_runtime_1.jsx)("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-1", children: [recipientEnsName && (0, jsx_runtime_1.jsxs)("span", { children: ["@", recipientEnsName] }), (0, jsx_runtime_1.jsx)("span", { children: (0, centerTruncate_1.default)(recipientAddress) })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRightCircle, { className: "ml-2 size-4 shrink-0 opacity-50" })] }) })] })) : null;
354
355
  const historyView = ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("mx-auto flex w-full max-w-2xl flex-col items-center p-5", mode === "modal" && "bg-b3-react-background"), children: (0, jsx_runtime_1.jsx)(OrderHistory_1.OrderHistory, { mode: mode, onBack: () => {
355
356
  setActivePanel(PanelView.HISTORY);
356
357
  }, onSelectOrder: onSelectOrder }) }));
@@ -408,20 +409,22 @@ function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", recipient
408
409
  }
409
410
  : undefined, recipientEnsName: recipientEnsName, recipientImageUrl: recipientImageUrl }) }) })] })] }));
410
411
  // Return the TransitionPanel with all views
411
- return ((0, jsx_runtime_1.jsx)(react_1.StyleRoot, { children: (0, jsx_runtime_1.jsx)(react_1.TransitionPanel, { activeIndex: orderId
412
- ? oat
413
- ? PanelView.ORDER_DETAILS
414
- : PanelView.LOADING
415
- : activePanel === PanelView.ORDER_DETAILS
416
- ? PanelView.CONFIRM_ORDER
417
- : activePanel, className: (0, utils_1.cn)("w-full"), variants: {
418
- enter: { x: 300, opacity: 0 },
419
- center: { x: 0, opacity: 1 },
420
- exit: { x: -300, opacity: 0 },
421
- }, transition: { type: "spring", stiffness: 300, damping: 30 }, children: [
422
- (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: confirmOrderView }, "edit-recipient-view"),
423
- (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: historyView }, "history-view"),
424
- (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: orderDetailsView }, "order-details-view"),
425
- (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: loadingView }, "loading-view"),
426
- ] }) }));
412
+ return ((0, jsx_runtime_1.jsxs)(react_1.StyleRoot, { children: [(0, jsx_runtime_1.jsx)(react_1.TransitionPanel, { activeIndex: orderId
413
+ ? oat
414
+ ? PanelView.ORDER_DETAILS
415
+ : PanelView.LOADING
416
+ : activePanel === PanelView.ORDER_DETAILS
417
+ ? PanelView.CONFIRM_ORDER
418
+ : activePanel, className: (0, utils_1.cn)("w-full"), variants: {
419
+ enter: { x: 300, opacity: 0 },
420
+ center: { x: 0, opacity: 1 },
421
+ exit: { x: -300, opacity: 0 },
422
+ }, transition: { type: "spring", stiffness: 300, damping: 30 }, children: [
423
+ (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: confirmOrderView }, "edit-recipient-view"),
424
+ (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: historyView }, "history-view"),
425
+ (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: orderDetailsView }, "order-details-view"),
426
+ (0, jsx_runtime_1.jsx)("div", { className: "w-full", children: loadingView }, "loading-view"),
427
+ ] }), (0, jsx_runtime_1.jsx)(react_1.Dialog, { open: isRecipientModalOpen, onOpenChange: setIsRecipientModalOpen, children: (0, jsx_runtime_1.jsx)(react_1.DialogContent, { className: "w-[420px] max-w-[calc(100vw-32px)] rounded-2xl p-3.5", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: "To address" }), (0, jsx_runtime_1.jsx)(react_1.Input, { value: customRecipientAddress || "", onChange: e => setCustomRecipientAddress(e.target.value), placeholder: "Enter address", className: "h-12 rounded-lg", spellCheck: false }), (0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "w-full rounded-lg", onClick: () => {
428
+ setIsRecipientModalOpen(false);
429
+ }, children: "Save" })] }) }) })] }));
427
430
  }
@@ -8,6 +8,7 @@ exports.OrderDetailsLoadingView = exports.OrderDetails = void 0;
8
8
  const jsx_runtime_1 = require("react/jsx-runtime");
9
9
  const anyspend_1 = require("../../../../anyspend");
10
10
  const react_1 = require("../../../../global-account/react");
11
+ const hooks_1 = require("../../../../shared/react/hooks");
11
12
  const utils_1 = require("../../../../shared/utils");
12
13
  const centerTruncate_1 = __importDefault(require("../../../../shared/utils/centerTruncate"));
13
14
  const number_1 = require("../../../../shared/utils/number");
@@ -15,7 +16,6 @@ const react_2 = require("@chakra-ui/react");
15
16
  const react_3 = require("@web3icons/react");
16
17
  const framer_motion_1 = require("framer-motion");
17
18
  const lucide_react_1 = require("lucide-react");
18
- const navigation_1 = require("next/navigation");
19
19
  const qrcode_react_1 = require("qrcode.react");
20
20
  const react_4 = require("react");
21
21
  const react_timeago_1 = __importDefault(require("react-timeago"));
@@ -121,8 +121,8 @@ function roundTokenAmount(amount) {
121
121
  return `${wholePart}.${roundedDecimalPart}`;
122
122
  }
123
123
  exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode = "modal", order, depositTxs, relayTx, executeTx, refundTxs, onBack, }) {
124
- const router = (0, navigation_1.useRouter)();
125
- const searchParams = (0, navigation_1.useSearchParams)();
124
+ const router = (0, hooks_1.useRouter)();
125
+ const searchParams = (0, hooks_1.useSearchParams)();
126
126
  const setB3ModalOpen = (0, react_1.useModalStore)(state => state.setB3ModalOpen);
127
127
  const srcToken = anyspend_1.zToken.parse(order.metadata.srcToken);
128
128
  const dstToken = anyspend_1.zToken.parse(order.metadata.dstToken);
@@ -227,6 +227,25 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
227
227
  params.set("waitingForDeposit", "true");
228
228
  router.push(`?${params}`);
229
229
  }, [router, searchParams]);
230
+ // Clean up URL parameters before closing modal or navigating back
231
+ const cleanupUrlParams = (0, react_4.useCallback)(() => {
232
+ const params = new URLSearchParams(searchParams.toString());
233
+ params.delete("waitingForDeposit");
234
+ params.delete("orderId");
235
+ // Only update URL if params were actually removed
236
+ if (params.toString() !== searchParams.toString()) {
237
+ router.push(`?${params}`);
238
+ }
239
+ }, [router, searchParams]);
240
+ // Helper functions that clean up URL params before executing actions
241
+ const handleCloseModal = (0, react_4.useCallback)(() => {
242
+ cleanupUrlParams();
243
+ setB3ModalOpen(false);
244
+ }, [cleanupUrlParams, setB3ModalOpen]);
245
+ const handleBack = (0, react_4.useCallback)(() => {
246
+ cleanupUrlParams();
247
+ onBack?.();
248
+ }, [cleanupUrlParams, onBack]);
230
249
  (0, react_4.useEffect)(() => {
231
250
  if (txSuccess) {
232
251
  sonner_1.toast.success("Transaction successful! We are processing your order.", { duration: 10000 });
@@ -279,7 +298,7 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
279
298
  : `Received ${(0, number_1.formatTokenAmount)(BigInt(dTx.amount), srcToken.decimals)} ${srcToken.symbol}`, chainId: order.srcChain, tx: dTx, isProcessing: false }, dTx.txHash)))
280
299
  : null, refundTxs
281
300
  ? refundTxs.map(rTx => ((0, jsx_runtime_1.jsx)(TransactionDetails, { title: `Refunded ${(0, number_1.formatTokenAmount)(BigInt(rTx.amount), srcToken.decimals)} ${srcToken.symbol}`, chainId: order.srcChain, tx: rTx, isProcessing: false }, rTx.txHash)))
282
- : null] }), order.errorDetails && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-center", children: (0, jsx_runtime_1.jsx)("span", { className: "text-as-primary/50 text-center text-sm", style: { maxWidth: "40ch" }, children: (0, anyspend_1.getErrorDisplay)(order.errorDetails) }) })), (0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") })] }));
301
+ : null] }), order.errorDetails && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-center", children: (0, jsx_runtime_1.jsx)("span", { className: "text-as-primary/50 text-center text-sm", style: { maxWidth: "40ch" }, children: (0, anyspend_1.getErrorDisplay)(order.errorDetails) }) })), (0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") })] }));
283
302
  }
284
303
  if (executeTx) {
285
304
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -301,7 +320,7 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
301
320
  dstToken,
302
321
  recipientName,
303
322
  centerTruncate: centerTruncate_1.default,
304
- }), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === anyspend_1.OrderType.JoinTournament && order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: () => setB3ModalOpen(false), children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4", children: "Continue to Tournament" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })), order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
323
+ }), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === anyspend_1.OrderType.JoinTournament && order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: handleCloseModal, children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4", children: "Continue to Tournament" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })), order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
305
324
  }
306
325
  if (relayTx && relayTx.status === "success") {
307
326
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -331,7 +350,7 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
331
350
  dstToken,
332
351
  recipientName,
333
352
  centerTruncate: centerTruncate_1.default,
334
- }), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === anyspend_1.OrderType.JoinTournament && order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: () => setB3ModalOpen(false), children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4", children: "Continue to Tournament" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })), order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
353
+ }), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === anyspend_1.OrderType.JoinTournament && order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: handleCloseModal, children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4", children: "Continue to Tournament" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })), order.status === anyspend_1.OrderStatus.Executed && ((0, jsx_runtime_1.jsx)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Return to Home ", (0, jsx_runtime_1.jsx)(lucide_react_1.Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
335
354
  }
336
355
  // This boolean indicates that user finish payment, and waiting for the deposit to be confirmed. We get this from query params (waitingForDeposit=true)
337
356
  const waitingForDeposit = new URLSearchParams(window.location.search).get("waitingForDeposit") === "true";
@@ -381,7 +400,7 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
381
400
  : "Contract execution"
382
401
  : "" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-end gap-2", children: [order.type === anyspend_1.OrderType.Swap ? (`~${formattedExpectedDstAmount} ${dstToken.symbol}`) : order.type === anyspend_1.OrderType.MintNFT ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("img", { src: nft?.imageUrl, alt: nft?.name || "NFT", className: "h-5 w-5" }), (0, jsx_runtime_1.jsx)("div", { children: nft?.name || "NFT" })] })) : order.type === anyspend_1.OrderType.JoinTournament || order.type === anyspend_1.OrderType.FundTournament ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("img", { src: tournament?.imageUrl, alt: tournament?.name || "Tournament", className: "h-5 w-5" }), (0, jsx_runtime_1.jsx)("div", { children: tournament?.name || "Tournament" })] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "text-as-primary/50 flex items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["on ", order.dstChain !== chains_1.b3.id && (0, anyspend_1.getChainName)(order.dstChain)] }), (0, jsx_runtime_1.jsx)("img", { src: anyspend_1.ALL_CHAINS[order.dstChain].logoUrl, alt: (0, anyspend_1.getChainName)(order.dstChain), className: (0, utils_1.cn)("h-3", order.dstChain !== chains_1.b3.id && "w-3 rounded-full", order.dstChain === chains_1.b3.id && "h-4") })] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full justify-between gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/30", children: "Order ID" }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap", children: order.id })] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full justify-between gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/30", children: "Recipient" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-end gap-1", children: [recipientName && (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: recipientName }), (0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: order.recipientAddress, onCopy: () => {
383
402
  sonner_1.toast.success("Copied recipient address to clipboard");
384
- }, children: (0, jsx_runtime_1.jsxs)("div", { className: "text-as-primary flex items-center gap-2", children: [(0, centerTruncate_1.default)(order.recipientAddress, 10), (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "text-as-primary/50 hover:text-as-primary h-4 w-4 cursor-pointer transition-all duration-200" })] }) })] })] })] }) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsx)("button", { className: "whitespace-nowrap text-sm", onClick: () => setShowOrderDetails(true), children: "Order Details" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "text-as-primary mx-1 h-4 min-h-4 w-4 min-w-4" }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" })] })), (0, jsx_runtime_1.jsxs)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: onBack, children: ["Cancel and start over ", (0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCcw, { className: "ml-2 h-4 w-4" })] })] }));
403
+ }, children: (0, jsx_runtime_1.jsxs)("div", { className: "text-as-primary flex items-center gap-2", children: [(0, centerTruncate_1.default)(order.recipientAddress, 10), (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "text-as-primary/50 hover:text-as-primary h-4 w-4 cursor-pointer transition-all duration-200" })] }) })] })] })] }) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsx)("button", { className: "whitespace-nowrap text-sm", onClick: () => setShowOrderDetails(true), children: "Order Details" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "text-as-primary mx-1 h-4 min-h-4 w-4 min-w-4" }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" })] })), (0, jsx_runtime_1.jsxs)("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: handleBack, children: ["Cancel and start over ", (0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCcw, { className: "ml-2 h-4 w-4" })] })] }));
385
404
  });
386
405
  function TransactionDetails({ title, chainId, tx, isProcessing, delay, }) {
387
406
  return ((0, jsx_runtime_1.jsxs)("div", { className: "relative flex w-full flex-1 items-center justify-between gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex grow items-center gap-4", children: [(0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: "bg-b3-react-background relative h-10 w-10 rounded-full", children: isProcessing ? ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { opacity: 0, scale: 0.3 }, animate: { opacity: 1, scale: 1 }, transition: { duration: 0.5, ease: "easeInOut", delay }, className: "absolute z-10 m-2 flex h-6 w-6 items-center justify-center rounded-full bg-black/70 shadow-lg backdrop-blur-sm", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "text-as-primary h-4 w-4 animate-spin" }) })) : ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { opacity: 0, scale: 0.3 }, animate: { opacity: 1, scale: 1 }, transition: { duration: 0.5, ease: "easeOut", delay }, className: "bg-as-brand/70 absolute z-10 m-2 flex h-6 w-6 items-center justify-center rounded-full border border-white/30 shadow-lg shadow-purple-500/30 backdrop-blur-sm", style: {
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EnterRecipientModal = EnterRecipientModal;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("react");
6
- const react_2 = require("../../../../global-account/react");
5
+ const react_1 = require("../../../../global-account/react");
6
+ const react_2 = require("react");
7
7
  function EnterRecipientModal({ isOpenPasteRecipientAddress, setIsOpenPasteRecipientAddress, recipientAddress, setRecipientAddress, }) {
8
- const [modalRecipientAddress, setModalRecipientAddress] = (0, react_1.useState)(recipientAddress || "");
9
- (0, react_1.useEffect)(() => {
8
+ const [modalRecipientAddress, setModalRecipientAddress] = (0, react_2.useState)(recipientAddress || "");
9
+ (0, react_2.useEffect)(() => {
10
10
  setModalRecipientAddress(recipientAddress || "");
11
11
  }, [recipientAddress]);
12
- return ((0, jsx_runtime_1.jsx)(react_2.Dialog, { open: isOpenPasteRecipientAddress, onOpenChange: setIsOpenPasteRecipientAddress, children: (0, jsx_runtime_1.jsx)(react_2.DialogContent, { className: "w-[420px] max-w-[calc(100vw-32px)] rounded-2xl p-3.5", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: "To address" }), (0, jsx_runtime_1.jsx)(react_2.Input, { value: modalRecipientAddress, onChange: e => setModalRecipientAddress(e.target.value), placeholder: "Enter address", className: "h-12 rounded-lg", spellCheck: false }), (0, jsx_runtime_1.jsx)(react_2.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "w-full rounded-lg", onClick: () => {
12
+ return ((0, jsx_runtime_1.jsx)(react_1.Dialog, { open: isOpenPasteRecipientAddress, onOpenChange: setIsOpenPasteRecipientAddress, children: (0, jsx_runtime_1.jsx)(react_1.DialogContent, { className: "w-[420px] max-w-[calc(100vw-32px)] rounded-2xl p-3.5", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: "To address" }), (0, jsx_runtime_1.jsx)(react_1.Input, { value: modalRecipientAddress, onChange: e => setModalRecipientAddress(e.target.value), placeholder: "Enter address", className: "h-12 rounded-lg", spellCheck: false }), (0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "w-full rounded-lg", onClick: () => {
13
13
  setIsOpenPasteRecipientAddress(false);
14
14
  setRecipientAddress(modalRecipientAddress);
15
15
  }, children: "Save" })] }) }) }));
@@ -62,9 +62,9 @@ function useAccountWallet() {
62
62
  smartWalletIcon,
63
63
  walletImage,
64
64
  ]);
65
- // useEffect(() => {
66
- // console.log(`account`, account);
67
- // console.log(`useAccountWallet`, res);
68
- // }, [account, res]);
65
+ (0, react_2.useEffect)(() => {
66
+ console.log(`account`, account);
67
+ console.log(`useAccountWallet`, res);
68
+ }, [account, res]);
69
69
  return res;
70
70
  }
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.useTokenFromUrl = useTokenFromUrl;
5
5
  exports.useTokenFromAddress = useTokenFromAddress;
6
6
  const supported_1 = require("../../../shared/constants/chains/supported");
7
+ const hooks_1 = require("../../../shared/react/hooks");
7
8
  const react_query_1 = require("@tanstack/react-query");
8
- const navigation_1 = require("next/navigation");
9
9
  async function fetchTokenInfo(network, address) {
10
10
  const response = await fetch("https://api.b3.fun/tokens", {
11
11
  method: "POST",
@@ -28,10 +28,10 @@ async function fetchTokenInfo(network, address) {
28
28
  * Looks for parameters: [prefix]Currency
29
29
  */
30
30
  function useTokenFromUrl({ defaultToken, prefix }) {
31
- const searchParams = (0, navigation_1.useSearchParams)();
31
+ const searchParams = (0, hooks_1.useSearchParams)();
32
32
  // Get parameters from URL
33
- const currencyParam = searchParams?.get(`${prefix}Currency`);
34
- const chainIdParam = searchParams?.get(`${prefix}ChainId`);
33
+ const currencyParam = searchParams.get(`${prefix}Currency`);
34
+ const chainIdParam = searchParams.get(`${prefix}ChainId`);
35
35
  // Determine if we should fetch token info
36
36
  const shouldFetchToken = Boolean(currencyParam && chainIdParam && currencyParam.toLowerCase() !== defaultToken.address.toLowerCase());
37
37
  // Determine network based on chainId
@@ -0,0 +1 @@
1
+ export * from "./useNavigation";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./useNavigation"), exports);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Framework-agnostic hook for reading URL search parameters
3
+ * Works with Next.js, Vite, and other React applications
4
+ */
5
+ export declare function useSearchParams(): URLSearchParams;
6
+ /**
7
+ * Framework-agnostic navigation utility
8
+ * Works with Next.js, Vite, and other React applications
9
+ */
10
+ export declare function useRouter(): {
11
+ push: (url: string) => void;
12
+ };
13
+ /**
14
+ * Direct utility function for getting search params without a hook
15
+ * Useful for server-side or one-time usage
16
+ */
17
+ export declare function getSearchParams(): URLSearchParams | null;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSearchParams = useSearchParams;
4
+ exports.useRouter = useRouter;
5
+ exports.getSearchParams = getSearchParams;
6
+ const react_1 = require("react");
7
+ /**
8
+ * Framework-agnostic hook for reading URL search parameters
9
+ * Works with Next.js, Vite, and other React applications
10
+ */
11
+ function useSearchParams() {
12
+ const [searchParams, setSearchParams] = (0, react_1.useState)(() => {
13
+ if (typeof window !== "undefined") {
14
+ return new URLSearchParams(window.location.search);
15
+ }
16
+ return new URLSearchParams();
17
+ });
18
+ (0, react_1.useEffect)(() => {
19
+ if (typeof window !== "undefined") {
20
+ const params = new URLSearchParams(window.location.search);
21
+ setSearchParams(params);
22
+ // Listen for URL changes (for client-side routing)
23
+ const handlePopState = () => {
24
+ setSearchParams(new URLSearchParams(window.location.search));
25
+ };
26
+ window.addEventListener("popstate", handlePopState);
27
+ return () => window.removeEventListener("popstate", handlePopState);
28
+ }
29
+ }, []);
30
+ return searchParams;
31
+ }
32
+ /**
33
+ * Framework-agnostic navigation utility
34
+ * Works with Next.js, Vite, and other React applications
35
+ */
36
+ function useRouter() {
37
+ const push = (url) => {
38
+ if (typeof window !== "undefined") {
39
+ // For client-side routing frameworks, we should use history.pushState
40
+ // This will work for most SPA frameworks
41
+ window.history.pushState({}, "", url);
42
+ // Dispatch a custom event that components can listen to
43
+ window.dispatchEvent(new PopStateEvent("popstate"));
44
+ }
45
+ };
46
+ return { push };
47
+ }
48
+ /**
49
+ * Direct utility function for getting search params without a hook
50
+ * Useful for server-side or one-time usage
51
+ */
52
+ function getSearchParams() {
53
+ if (typeof window !== "undefined") {
54
+ return new URLSearchParams(window.location.search);
55
+ }
56
+ return null;
57
+ }
@@ -0,0 +1 @@
1
+ export * from "./hooks";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./hooks"), exports);
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { eqci, getDefaultToken, isCustomTxMetadata, isNftMetadata, isTournamentMetadata, NftType, OrderStatus, OrderType, RELAY_ETH_ADDRESS, USDC_BASE, useAnyspendCreateOnrampOrder, useAnyspendCreateOrder, useAnyspendOrderAndTransactions, useAnyspendQuote, useAnyspendTokenList, useGeoOnrampOptions, } from "../../../anyspend/index.js";
3
- import { Badge, Button, ShinyButton, Skeleton, StyleRoot, Tabs, TabsContent, TabsList, TabTrigger, TextShimmer, TransitionPanel, useAccountWallet, useB3, useBsmntProfile, useHasMounted, useModalStore, useRouter, useSearchParamsSSR, useTokenBalancesByChain, } from "../../../global-account/react/index.js";
3
+ import { Badge, Button, ShinyButton, Skeleton, StyleRoot, Tabs, TabsContent, TabsList, TabTrigger, TextShimmer, TransitionPanel, useAccountWallet, useB3, useBsmntProfile, useHasMounted, useModalStore, useRouter, useSearchParamsSSR, useTokenBalancesByChain, Dialog, DialogContent, Input, } from "../../../global-account/react/index.js";
4
4
  import { cn } from "../../../shared/utils/index.js";
5
5
  import centerTruncate from "../../../shared/utils/centerTruncate.js";
6
6
  import { formatTokenAmount } from "../../../shared/utils/number.js";
@@ -89,14 +89,15 @@ export function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", re
89
89
  const [activeTab, setActiveTab] = useState("crypto");
90
90
  // Get current user's wallet
91
91
  const currentWallet = useAccountWallet();
92
- const recipientPropsProfile = useBsmntProfile({ address: recipientAddressProps });
93
- const recipientAddress = recipientAddressProps || currentWallet.address;
94
- const recipientEnsName = recipientAddressProps
95
- ? recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "")
96
- : currentWallet.ensName;
97
- const recipientImageUrl = recipientAddressProps
98
- ? recipientPropsProfile.data?.avatar
99
- : currentWallet.wallet.meta?.icon;
92
+ // Add state for recipient modal
93
+ const [isRecipientModalOpen, setIsRecipientModalOpen] = useState(false);
94
+ // Add state for custom recipient
95
+ const [customRecipientAddress, setCustomRecipientAddress] = useState(recipientAddressProps);
96
+ // Update recipient logic to use custom recipient
97
+ const recipientAddress = customRecipientAddress || currentWallet.address;
98
+ const recipientPropsProfile = useBsmntProfile({ address: recipientAddress });
99
+ const recipientEnsName = recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "");
100
+ const recipientImageUrl = recipientPropsProfile.data?.avatar || currentWallet.wallet.meta?.icon;
100
101
  const [orderId, setOrderId] = useState(loadOrder);
101
102
  const [srcChainId, setSrcChainId] = useState(isMainnet ? base.id : baseSepolia.id);
102
103
  // Get token list for token balance check
@@ -344,7 +345,7 @@ export function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", re
344
345
  ? "Receive NFT at"
345
346
  : orderType === OrderType.JoinTournament
346
347
  ? "Join for"
347
- : "Recipient" }), _jsx("div", { children: _jsxs(Button, { variant: "outline", className: "w-full justify-between border-none p-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [recipientImageUrl && (_jsx("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), _jsxs("div", { className: "flex flex-col items-start gap-1", children: [recipientEnsName && _jsxs("span", { children: ["@", recipientEnsName] }), _jsx("span", { children: centerTruncate(recipientAddress) })] })] }), _jsx(ChevronRightCircle, { className: "ml-2 size-4 shrink-0 opacity-50" })] }) })] })) : null;
348
+ : "Recipient" }), _jsx("div", { children: _jsxs(Button, { variant: "outline", className: "w-full justify-between border-none p-0", onClick: () => setIsRecipientModalOpen(true), children: [_jsxs("div", { className: "flex items-center gap-2", children: [recipientImageUrl && (_jsx("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), _jsxs("div", { className: "flex flex-col items-start gap-1", children: [recipientEnsName && _jsxs("span", { children: ["@", recipientEnsName] }), _jsx("span", { children: centerTruncate(recipientAddress) })] })] }), _jsx(ChevronRightCircle, { className: "ml-2 size-4 shrink-0 opacity-50" })] }) })] })) : null;
348
349
  const historyView = (_jsx("div", { className: cn("mx-auto flex w-full max-w-2xl flex-col items-center p-5", mode === "modal" && "bg-b3-react-background"), children: _jsx(OrderHistory, { mode: mode, onBack: () => {
349
350
  setActivePanel(PanelView.HISTORY);
350
351
  }, onSelectOrder: onSelectOrder }) }));
@@ -402,20 +403,22 @@ export function AnySpendCustom({ isMainnet = true, loadOrder, mode = "modal", re
402
403
  }
403
404
  : undefined, recipientEnsName: recipientEnsName, recipientImageUrl: recipientImageUrl }) }) })] })] }));
404
405
  // Return the TransitionPanel with all views
405
- return (_jsx(StyleRoot, { children: _jsx(TransitionPanel, { activeIndex: orderId
406
- ? oat
407
- ? PanelView.ORDER_DETAILS
408
- : PanelView.LOADING
409
- : activePanel === PanelView.ORDER_DETAILS
410
- ? PanelView.CONFIRM_ORDER
411
- : activePanel, className: cn("w-full"), variants: {
412
- enter: { x: 300, opacity: 0 },
413
- center: { x: 0, opacity: 1 },
414
- exit: { x: -300, opacity: 0 },
415
- }, transition: { type: "spring", stiffness: 300, damping: 30 }, children: [
416
- _jsx("div", { className: "w-full", children: confirmOrderView }, "edit-recipient-view"),
417
- _jsx("div", { className: "w-full", children: historyView }, "history-view"),
418
- _jsx("div", { className: "w-full", children: orderDetailsView }, "order-details-view"),
419
- _jsx("div", { className: "w-full", children: loadingView }, "loading-view"),
420
- ] }) }));
406
+ return (_jsxs(StyleRoot, { children: [_jsx(TransitionPanel, { activeIndex: orderId
407
+ ? oat
408
+ ? PanelView.ORDER_DETAILS
409
+ : PanelView.LOADING
410
+ : activePanel === PanelView.ORDER_DETAILS
411
+ ? PanelView.CONFIRM_ORDER
412
+ : activePanel, className: cn("w-full"), variants: {
413
+ enter: { x: 300, opacity: 0 },
414
+ center: { x: 0, opacity: 1 },
415
+ exit: { x: -300, opacity: 0 },
416
+ }, transition: { type: "spring", stiffness: 300, damping: 30 }, children: [
417
+ _jsx("div", { className: "w-full", children: confirmOrderView }, "edit-recipient-view"),
418
+ _jsx("div", { className: "w-full", children: historyView }, "history-view"),
419
+ _jsx("div", { className: "w-full", children: orderDetailsView }, "order-details-view"),
420
+ _jsx("div", { className: "w-full", children: loadingView }, "loading-view"),
421
+ ] }), _jsx(Dialog, { open: isRecipientModalOpen, onOpenChange: setIsRecipientModalOpen, children: _jsx(DialogContent, { className: "w-[420px] max-w-[calc(100vw-32px)] rounded-2xl p-3.5", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { className: "text-as-primary font-semibold", children: "To address" }), _jsx(Input, { value: customRecipientAddress || "", onChange: e => setCustomRecipientAddress(e.target.value), placeholder: "Enter address", className: "h-12 rounded-lg", spellCheck: false }), _jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "w-full rounded-lg", onClick: () => {
422
+ setIsRecipientModalOpen(false);
423
+ }, children: "Save" })] }) }) })] }));
421
424
  }
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { ALL_CHAINS, capitalizeFirstLetter, EVM_CHAINS, getChainName, getErrorDisplay, getExplorerTxUrl, getPaymentUrl, getStatusDisplay, isNativeToken, OnrampVendor, OrderStatus, OrderType, RELAY_ETH_ADDRESS, zNft, zToken, zTournament, } from "../../../../anyspend/index.js";
4
4
  import { Badge, Button, CopyToClipboard, ShinyButton, Skeleton, TextLoop, TextShimmer, useAccountWallet, useChainSwitchWithAction, useModalStore, useOnchainName, } from "../../../../global-account/react/index.js";
5
+ import { useRouter, useSearchParams } from "../../../../shared/react/hooks/index.js";
5
6
  import { cn } from "../../../../shared/utils/index.js";
6
7
  import centerTruncate from "../../../../shared/utils/centerTruncate.js";
7
8
  import { formatTokenAmount } from "../../../../shared/utils/number.js";
@@ -9,7 +10,6 @@ import { useColorMode } from "@chakra-ui/react";
9
10
  import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletTrust, WalletWalletConnect } from "@web3icons/react";
10
11
  import { motion } from "framer-motion";
11
12
  import { CheckIcon, ChevronDown, ChevronRight, Copy, ExternalLink, Home, Loader2, RefreshCcw, SquareArrowOutUpRight, } from "lucide-react";
12
- import { useRouter, useSearchParams } from "next/navigation";
13
13
  import { QRCodeSVG } from "qrcode.react";
14
14
  import { memo, useCallback, useEffect, useMemo, useState } from "react";
15
15
  import TimeAgo from "react-timeago";
@@ -221,6 +221,25 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
221
221
  params.set("waitingForDeposit", "true");
222
222
  router.push(`?${params}`);
223
223
  }, [router, searchParams]);
224
+ // Clean up URL parameters before closing modal or navigating back
225
+ const cleanupUrlParams = useCallback(() => {
226
+ const params = new URLSearchParams(searchParams.toString());
227
+ params.delete("waitingForDeposit");
228
+ params.delete("orderId");
229
+ // Only update URL if params were actually removed
230
+ if (params.toString() !== searchParams.toString()) {
231
+ router.push(`?${params}`);
232
+ }
233
+ }, [router, searchParams]);
234
+ // Helper functions that clean up URL params before executing actions
235
+ const handleCloseModal = useCallback(() => {
236
+ cleanupUrlParams();
237
+ setB3ModalOpen(false);
238
+ }, [cleanupUrlParams, setB3ModalOpen]);
239
+ const handleBack = useCallback(() => {
240
+ cleanupUrlParams();
241
+ onBack?.();
242
+ }, [cleanupUrlParams, onBack]);
224
243
  useEffect(() => {
225
244
  if (txSuccess) {
226
245
  toast.success("Transaction successful! We are processing your order.", { duration: 10000 });
@@ -273,7 +292,7 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
273
292
  : `Received ${formatTokenAmount(BigInt(dTx.amount), srcToken.decimals)} ${srcToken.symbol}`, chainId: order.srcChain, tx: dTx, isProcessing: false }, dTx.txHash)))
274
293
  : null, refundTxs
275
294
  ? refundTxs.map(rTx => (_jsx(TransactionDetails, { title: `Refunded ${formatTokenAmount(BigInt(rTx.amount), srcToken.decimals)} ${srcToken.symbol}`, chainId: order.srcChain, tx: rTx, isProcessing: false }, rTx.txHash)))
276
- : null] }), order.errorDetails && (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "text-as-primary/50 text-center text-sm", style: { maxWidth: "40ch" }, children: getErrorDisplay(order.errorDetails) }) })), _jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") })] }));
295
+ : null] }), order.errorDetails && (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "text-as-primary/50 text-center text-sm", style: { maxWidth: "40ch" }, children: getErrorDisplay(order.errorDetails) }) })), _jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") })] }));
277
296
  }
278
297
  if (executeTx) {
279
298
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [_jsx("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: _jsx(motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -295,7 +314,7 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
295
314
  dstToken,
296
315
  recipientName,
297
316
  centerTruncate,
298
- }), _jsx(ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === OrderType.JoinTournament && order.status === OrderStatus.Executed && (_jsxs(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: () => setB3ModalOpen(false), children: [_jsx("span", { className: "pl-4", children: "Continue to Tournament" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })), order.status === OrderStatus.Executed && (_jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
317
+ }), _jsx(ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === OrderType.JoinTournament && order.status === OrderStatus.Executed && (_jsxs(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: handleCloseModal, children: [_jsx("span", { className: "pl-4", children: "Continue to Tournament" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })), order.status === OrderStatus.Executed && (_jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
299
318
  }
300
319
  if (relayTx && relayTx.status === "success") {
301
320
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [_jsx("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: _jsx(motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -325,7 +344,7 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
325
344
  dstToken,
326
345
  recipientName,
327
346
  centerTruncate,
328
- }), _jsx(ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === OrderType.JoinTournament && order.status === OrderStatus.Executed && (_jsxs(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: () => setB3ModalOpen(false), children: [_jsx("span", { className: "pl-4", children: "Continue to Tournament" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })), order.status === OrderStatus.Executed && (_jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? onBack : () => setB3ModalOpen(false), children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
347
+ }), _jsx(ExternalLink, { className: "ml-2 h-4 w-4" })] }) }) }), order.type === OrderType.JoinTournament && order.status === OrderStatus.Executed && (_jsxs(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-full items-center gap-2", disabled: txLoading || isSwitchingOrExecuting, onClick: handleCloseModal, children: [_jsx("span", { className: "pl-4", children: "Continue to Tournament" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })), order.status === OrderStatus.Executed && (_jsx("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: mode === "page" ? handleBack : handleCloseModal, children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") }))] }));
329
348
  }
330
349
  // This boolean indicates that user finish payment, and waiting for the deposit to be confirmed. We get this from query params (waitingForDeposit=true)
331
350
  const waitingForDeposit = new URLSearchParams(window.location.search).get("waitingForDeposit") === "true";
@@ -375,7 +394,7 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
375
394
  : "Contract execution"
376
395
  : "" }), _jsxs("div", { className: "flex items-end gap-2", children: [order.type === OrderType.Swap ? (`~${formattedExpectedDstAmount} ${dstToken.symbol}`) : order.type === OrderType.MintNFT ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("img", { src: nft?.imageUrl, alt: nft?.name || "NFT", className: "h-5 w-5" }), _jsx("div", { children: nft?.name || "NFT" })] })) : order.type === OrderType.JoinTournament || order.type === OrderType.FundTournament ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("img", { src: tournament?.imageUrl, alt: tournament?.name || "Tournament", className: "h-5 w-5" }), _jsx("div", { children: tournament?.name || "Tournament" })] })) : null, _jsxs("div", { className: "text-as-primary/50 flex items-center gap-2", children: [_jsxs("span", { children: ["on ", order.dstChain !== b3.id && getChainName(order.dstChain)] }), _jsx("img", { src: ALL_CHAINS[order.dstChain].logoUrl, alt: getChainName(order.dstChain), className: cn("h-3", order.dstChain !== b3.id && "w-3 rounded-full", order.dstChain === b3.id && "h-4") })] })] })] }), _jsx("div", { className: "divider w-full" }), _jsxs("div", { className: "flex w-full justify-between gap-4", children: [_jsx("div", { className: "text-as-primary/30", children: "Order ID" }), _jsx("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap", children: order.id })] }), _jsx("div", { className: "divider w-full" }), _jsxs("div", { className: "flex w-full justify-between gap-4", children: [_jsx("div", { className: "text-as-primary/30", children: "Recipient" }), _jsxs("div", { className: "flex flex-col items-end gap-1", children: [recipientName && _jsx("div", { className: "text-as-primary font-semibold", children: recipientName }), _jsx(CopyToClipboard, { text: order.recipientAddress, onCopy: () => {
377
396
  toast.success("Copied recipient address to clipboard");
378
- }, children: _jsxs("div", { className: "text-as-primary flex items-center gap-2", children: [centerTruncate(order.recipientAddress, 10), _jsx(Copy, { className: "text-as-primary/50 hover:text-as-primary h-4 w-4 cursor-pointer transition-all duration-200" })] }) })] })] })] }) })) : (_jsxs("div", { className: "flex w-full items-center", children: [_jsx("div", { className: "divider w-full" }), _jsx("button", { className: "whitespace-nowrap text-sm", onClick: () => setShowOrderDetails(true), children: "Order Details" }), _jsx(ChevronDown, { className: "text-as-primary mx-1 h-4 min-h-4 w-4 min-w-4" }), _jsx("div", { className: "divider w-full" })] })), _jsxs("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: onBack, children: ["Cancel and start over ", _jsx(RefreshCcw, { className: "ml-2 h-4 w-4" })] })] }));
397
+ }, children: _jsxs("div", { className: "text-as-primary flex items-center gap-2", children: [centerTruncate(order.recipientAddress, 10), _jsx(Copy, { className: "text-as-primary/50 hover:text-as-primary h-4 w-4 cursor-pointer transition-all duration-200" })] }) })] })] })] }) })) : (_jsxs("div", { className: "flex w-full items-center", children: [_jsx("div", { className: "divider w-full" }), _jsx("button", { className: "whitespace-nowrap text-sm", onClick: () => setShowOrderDetails(true), children: "Order Details" }), _jsx(ChevronDown, { className: "text-as-primary mx-1 h-4 min-h-4 w-4 min-w-4" }), _jsx("div", { className: "divider w-full" })] })), _jsxs("button", { className: "bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2", onClick: handleBack, children: ["Cancel and start over ", _jsx(RefreshCcw, { className: "ml-2 h-4 w-4" })] })] }));
379
398
  });
380
399
  function TransactionDetails({ title, chainId, tx, isProcessing, delay, }) {
381
400
  return (_jsxs("div", { className: "relative flex w-full flex-1 items-center justify-between gap-4", children: [_jsxs("div", { className: "flex grow items-center gap-4", children: [_jsx(motion.div, { className: "bg-b3-react-background relative h-10 w-10 rounded-full", children: isProcessing ? (_jsx(motion.div, { initial: { opacity: 0, scale: 0.3 }, animate: { opacity: 1, scale: 1 }, transition: { duration: 0.5, ease: "easeInOut", delay }, className: "absolute z-10 m-2 flex h-6 w-6 items-center justify-center rounded-full bg-black/70 shadow-lg backdrop-blur-sm", children: _jsx(Loader2, { className: "text-as-primary h-4 w-4 animate-spin" }) })) : (_jsx(motion.div, { initial: { opacity: 0, scale: 0.3 }, animate: { opacity: 1, scale: 1 }, transition: { duration: 0.5, ease: "easeOut", delay }, className: "bg-as-brand/70 absolute z-10 m-2 flex h-6 w-6 items-center justify-center rounded-full border border-white/30 shadow-lg shadow-purple-500/30 backdrop-blur-sm", style: {
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
2
  import { Dialog, DialogContent, Input, ShinyButton } from "../../../../global-account/react/index.js";
3
+ import { useEffect, useState } from "react";
4
4
  export function EnterRecipientModal({ isOpenPasteRecipientAddress, setIsOpenPasteRecipientAddress, recipientAddress, setRecipientAddress, }) {
5
5
  const [modalRecipientAddress, setModalRecipientAddress] = useState(recipientAddress || "");
6
6
  useEffect(() => {
@@ -59,9 +59,9 @@ export function useAccountWallet() {
59
59
  smartWalletIcon,
60
60
  walletImage,
61
61
  ]);
62
- // useEffect(() => {
63
- // console.log(`account`, account);
64
- // console.log(`useAccountWallet`, res);
65
- // }, [account, res]);
62
+ useEffect(() => {
63
+ console.log(`account`, account);
64
+ console.log(`useAccountWallet`, res);
65
+ }, [account, res]);
66
66
  return res;
67
67
  }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { getCoingeckoChainInfo } from "../../../shared/constants/chains/supported.js";
3
+ import { useSearchParams } from "../../../shared/react/hooks/index.js";
3
4
  import { useQuery } from "@tanstack/react-query";
4
- import { useSearchParams } from "next/navigation";
5
5
  async function fetchTokenInfo(network, address) {
6
6
  const response = await fetch("https://api.b3.fun/tokens", {
7
7
  method: "POST",
@@ -26,8 +26,8 @@ async function fetchTokenInfo(network, address) {
26
26
  export function useTokenFromUrl({ defaultToken, prefix }) {
27
27
  const searchParams = useSearchParams();
28
28
  // Get parameters from URL
29
- const currencyParam = searchParams?.get(`${prefix}Currency`);
30
- const chainIdParam = searchParams?.get(`${prefix}ChainId`);
29
+ const currencyParam = searchParams.get(`${prefix}Currency`);
30
+ const chainIdParam = searchParams.get(`${prefix}ChainId`);
31
31
  // Determine if we should fetch token info
32
32
  const shouldFetchToken = Boolean(currencyParam && chainIdParam && currencyParam.toLowerCase() !== defaultToken.address.toLowerCase());
33
33
  // Determine network based on chainId
@@ -0,0 +1 @@
1
+ export * from "./useNavigation";
@@ -0,0 +1 @@
1
+ export * from "./useNavigation.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Framework-agnostic hook for reading URL search parameters
3
+ * Works with Next.js, Vite, and other React applications
4
+ */
5
+ export declare function useSearchParams(): URLSearchParams;
6
+ /**
7
+ * Framework-agnostic navigation utility
8
+ * Works with Next.js, Vite, and other React applications
9
+ */
10
+ export declare function useRouter(): {
11
+ push: (url: string) => void;
12
+ };
13
+ /**
14
+ * Direct utility function for getting search params without a hook
15
+ * Useful for server-side or one-time usage
16
+ */
17
+ export declare function getSearchParams(): URLSearchParams | null;
@@ -0,0 +1,52 @@
1
+ import { useEffect, useState } from "react";
2
+ /**
3
+ * Framework-agnostic hook for reading URL search parameters
4
+ * Works with Next.js, Vite, and other React applications
5
+ */
6
+ export function useSearchParams() {
7
+ const [searchParams, setSearchParams] = useState(() => {
8
+ if (typeof window !== "undefined") {
9
+ return new URLSearchParams(window.location.search);
10
+ }
11
+ return new URLSearchParams();
12
+ });
13
+ useEffect(() => {
14
+ if (typeof window !== "undefined") {
15
+ const params = new URLSearchParams(window.location.search);
16
+ setSearchParams(params);
17
+ // Listen for URL changes (for client-side routing)
18
+ const handlePopState = () => {
19
+ setSearchParams(new URLSearchParams(window.location.search));
20
+ };
21
+ window.addEventListener("popstate", handlePopState);
22
+ return () => window.removeEventListener("popstate", handlePopState);
23
+ }
24
+ }, []);
25
+ return searchParams;
26
+ }
27
+ /**
28
+ * Framework-agnostic navigation utility
29
+ * Works with Next.js, Vite, and other React applications
30
+ */
31
+ export function useRouter() {
32
+ const push = (url) => {
33
+ if (typeof window !== "undefined") {
34
+ // For client-side routing frameworks, we should use history.pushState
35
+ // This will work for most SPA frameworks
36
+ window.history.pushState({}, "", url);
37
+ // Dispatch a custom event that components can listen to
38
+ window.dispatchEvent(new PopStateEvent("popstate"));
39
+ }
40
+ };
41
+ return { push };
42
+ }
43
+ /**
44
+ * Direct utility function for getting search params without a hook
45
+ * Useful for server-side or one-time usage
46
+ */
47
+ export function getSearchParams() {
48
+ if (typeof window !== "undefined") {
49
+ return new URLSearchParams(window.location.search);
50
+ }
51
+ return null;
52
+ }
@@ -0,0 +1 @@
1
+ export * from "./hooks";
@@ -0,0 +1 @@
1
+ export * from "./hooks/index.js";
@@ -0,0 +1 @@
1
+ export * from "./useNavigation";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Framework-agnostic hook for reading URL search parameters
3
+ * Works with Next.js, Vite, and other React applications
4
+ */
5
+ export declare function useSearchParams(): URLSearchParams;
6
+ /**
7
+ * Framework-agnostic navigation utility
8
+ * Works with Next.js, Vite, and other React applications
9
+ */
10
+ export declare function useRouter(): {
11
+ push: (url: string) => void;
12
+ };
13
+ /**
14
+ * Direct utility function for getting search params without a hook
15
+ * Useful for server-side or one-time usage
16
+ */
17
+ export declare function getSearchParams(): URLSearchParams | null;
@@ -0,0 +1 @@
1
+ export * from "./hooks";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.7-alpha.16",
3
+ "version": "0.0.7-alpha.18",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -43,6 +43,9 @@ import {
43
43
  useRouter,
44
44
  useSearchParamsSSR,
45
45
  useTokenBalancesByChain,
46
+ Dialog,
47
+ DialogContent,
48
+ Input,
46
49
  } from "@b3dotfun/sdk/global-account/react";
47
50
  import { cn } from "@b3dotfun/sdk/shared/utils";
48
51
  import centerTruncate from "@b3dotfun/sdk/shared/utils/centerTruncate";
@@ -203,15 +206,17 @@ export function AnySpendCustom({
203
206
  // Get current user's wallet
204
207
  const currentWallet = useAccountWallet();
205
208
 
206
- const recipientPropsProfile = useBsmntProfile({ address: recipientAddressProps });
209
+ // Add state for recipient modal
210
+ const [isRecipientModalOpen, setIsRecipientModalOpen] = useState(false);
207
211
 
208
- const recipientAddress = recipientAddressProps || currentWallet.address;
209
- const recipientEnsName = recipientAddressProps
210
- ? recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "")
211
- : currentWallet.ensName;
212
- const recipientImageUrl = recipientAddressProps
213
- ? recipientPropsProfile.data?.avatar
214
- : currentWallet.wallet.meta?.icon;
212
+ // Add state for custom recipient
213
+ const [customRecipientAddress, setCustomRecipientAddress] = useState<string | undefined>(recipientAddressProps);
214
+
215
+ // Update recipient logic to use custom recipient
216
+ const recipientAddress = customRecipientAddress || currentWallet.address;
217
+ const recipientPropsProfile = useBsmntProfile({ address: recipientAddress });
218
+ const recipientEnsName = recipientPropsProfile.data?.username?.replaceAll(".b3.fun", "");
219
+ const recipientImageUrl = recipientPropsProfile.data?.avatar || currentWallet.wallet.meta?.icon;
215
220
 
216
221
  const [orderId, setOrderId] = useState<string | undefined>(loadOrder);
217
222
 
@@ -505,7 +510,11 @@ export function AnySpendCustom({
505
510
  : "Recipient"}
506
511
  </div>
507
512
  <div>
508
- <Button variant="outline" className="w-full justify-between border-none p-0">
513
+ <Button
514
+ variant="outline"
515
+ className="w-full justify-between border-none p-0"
516
+ onClick={() => setIsRecipientModalOpen(true)}
517
+ >
509
518
  <div className="flex items-center gap-2">
510
519
  {recipientImageUrl && (
511
520
  <img
@@ -864,6 +873,32 @@ export function AnySpendCustom({
864
873
  </div>,
865
874
  ]}
866
875
  </TransitionPanel>
876
+
877
+ {/* Add EnterRecipientModal */}
878
+ <Dialog open={isRecipientModalOpen} onOpenChange={setIsRecipientModalOpen}>
879
+ <DialogContent className="w-[420px] max-w-[calc(100vw-32px)] rounded-2xl p-3.5">
880
+ <div className="flex flex-col gap-3">
881
+ <div className="text-as-primary font-semibold">To address</div>
882
+ <Input
883
+ value={customRecipientAddress || ""}
884
+ onChange={e => setCustomRecipientAddress(e.target.value)}
885
+ placeholder="Enter address"
886
+ className="h-12 rounded-lg"
887
+ spellCheck={false}
888
+ />
889
+ <ShinyButton
890
+ accentColor={"hsl(var(--as-brand))"}
891
+ textColor="text-white"
892
+ className="w-full rounded-lg"
893
+ onClick={() => {
894
+ setIsRecipientModalOpen(false);
895
+ }}
896
+ >
897
+ Save
898
+ </ShinyButton>
899
+ </div>
900
+ </DialogContent>
901
+ </Dialog>
867
902
  </StyleRoot>
868
903
  );
869
904
  }
@@ -36,6 +36,7 @@ import {
36
36
  useModalStore,
37
37
  useOnchainName,
38
38
  } from "@b3dotfun/sdk/global-account/react";
39
+ import { useRouter, useSearchParams } from "@b3dotfun/sdk/shared/react/hooks";
39
40
  import { cn } from "@b3dotfun/sdk/shared/utils";
40
41
  import centerTruncate from "@b3dotfun/sdk/shared/utils/centerTruncate";
41
42
  import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
@@ -53,7 +54,6 @@ import {
53
54
  RefreshCcw,
54
55
  SquareArrowOutUpRight,
55
56
  } from "lucide-react";
56
- import { useRouter, useSearchParams } from "next/navigation";
57
57
  import { QRCodeSVG } from "qrcode.react";
58
58
  import { memo, useCallback, useEffect, useMemo, useState } from "react";
59
59
  import TimeAgo from "react-timeago";
@@ -340,6 +340,29 @@ export const OrderDetails = memo(function OrderDetails({
340
340
  router.push(`?${params}`);
341
341
  }, [router, searchParams]);
342
342
 
343
+ // Clean up URL parameters before closing modal or navigating back
344
+ const cleanupUrlParams = useCallback(() => {
345
+ const params = new URLSearchParams(searchParams.toString());
346
+ params.delete("waitingForDeposit");
347
+ params.delete("orderId");
348
+
349
+ // Only update URL if params were actually removed
350
+ if (params.toString() !== searchParams.toString()) {
351
+ router.push(`?${params}`);
352
+ }
353
+ }, [router, searchParams]);
354
+
355
+ // Helper functions that clean up URL params before executing actions
356
+ const handleCloseModal = useCallback(() => {
357
+ cleanupUrlParams();
358
+ setB3ModalOpen(false);
359
+ }, [cleanupUrlParams, setB3ModalOpen]);
360
+
361
+ const handleBack = useCallback(() => {
362
+ cleanupUrlParams();
363
+ onBack?.();
364
+ }, [cleanupUrlParams, onBack]);
365
+
343
366
  useEffect(() => {
344
367
  if (txSuccess) {
345
368
  toast.success("Transaction successful! We are processing your order.", { duration: 10000 });
@@ -446,7 +469,7 @@ export const OrderDetails = memo(function OrderDetails({
446
469
  )}
447
470
  <button
448
471
  className="bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2"
449
- onClick={mode === "page" ? onBack : () => setB3ModalOpen(false)}
472
+ onClick={mode === "page" ? handleBack : handleCloseModal}
450
473
  >
451
474
  {mode === "page" ? (
452
475
  <>
@@ -539,7 +562,7 @@ export const OrderDetails = memo(function OrderDetails({
539
562
  textColor="text-white"
540
563
  className="flex w-full items-center gap-2"
541
564
  disabled={txLoading || isSwitchingOrExecuting}
542
- onClick={() => setB3ModalOpen(false)}
565
+ onClick={handleCloseModal}
543
566
  >
544
567
  <span className="pl-4">Continue to Tournament</span>
545
568
  <ChevronRight className="h-4 w-4" />
@@ -549,7 +572,7 @@ export const OrderDetails = memo(function OrderDetails({
549
572
  {order.status === OrderStatus.Executed && (
550
573
  <button
551
574
  className="bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2"
552
- onClick={mode === "page" ? onBack : () => setB3ModalOpen(false)}
575
+ onClick={mode === "page" ? handleBack : handleCloseModal}
553
576
  >
554
577
  {mode === "page" ? (
555
578
  <>
@@ -667,7 +690,7 @@ export const OrderDetails = memo(function OrderDetails({
667
690
  textColor="text-white"
668
691
  className="flex w-full items-center gap-2"
669
692
  disabled={txLoading || isSwitchingOrExecuting}
670
- onClick={() => setB3ModalOpen(false)}
693
+ onClick={handleCloseModal}
671
694
  >
672
695
  <span className="pl-4">Continue to Tournament</span>
673
696
  <ChevronRight className="h-4 w-4" />
@@ -677,7 +700,7 @@ export const OrderDetails = memo(function OrderDetails({
677
700
  {order.status === OrderStatus.Executed && (
678
701
  <button
679
702
  className="bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2"
680
- onClick={mode === "page" ? onBack : () => setB3ModalOpen(false)}
703
+ onClick={mode === "page" ? handleBack : handleCloseModal}
681
704
  >
682
705
  {mode === "page" ? (
683
706
  <>
@@ -1075,7 +1098,7 @@ export const OrderDetails = memo(function OrderDetails({
1075
1098
 
1076
1099
  <button
1077
1100
  className="bg-as-on-surface-2 text-as-secondary flex w-full items-center justify-center gap-2 rounded-lg p-2"
1078
- onClick={onBack}
1101
+ onClick={handleBack}
1079
1102
  >
1080
1103
  Cancel and start over <RefreshCcw className="ml-2 h-4 w-4" />
1081
1104
  </button>
@@ -1,5 +1,5 @@
1
- import { useEffect, useState } from "react";
2
1
  import { Dialog, DialogContent, Input, ShinyButton } from "@b3dotfun/sdk/global-account/react";
2
+ import { useEffect, useState } from "react";
3
3
 
4
4
  export function EnterRecipientModal({
5
5
  isOpenPasteRecipientAddress,
@@ -101,10 +101,10 @@ export function useAccountWallet(): {
101
101
  ],
102
102
  );
103
103
 
104
- // useEffect(() => {
105
- // console.log(`account`, account);
106
- // console.log(`useAccountWallet`, res);
107
- // }, [account, res]);
104
+ useEffect(() => {
105
+ console.log(`account`, account);
106
+ console.log(`useAccountWallet`, res);
107
+ }, [account, res]);
108
108
 
109
109
  return res;
110
110
  }
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { Token } from "@b3dotfun/sdk/anyspend";
4
4
  import { getCoingeckoChainInfo } from "@b3dotfun/sdk/shared/constants/chains/supported";
5
+ import { useSearchParams } from "@b3dotfun/sdk/shared/react/hooks";
5
6
  import { useQuery } from "@tanstack/react-query";
6
- import { useSearchParams } from "next/navigation";
7
7
 
8
8
  interface UseTokenFromUrlOptions {
9
9
  /**
@@ -56,8 +56,8 @@ export function useTokenFromUrl({ defaultToken, prefix }: UseTokenFromUrlOptions
56
56
  const searchParams = useSearchParams();
57
57
 
58
58
  // Get parameters from URL
59
- const currencyParam = searchParams?.get(`${prefix}Currency`);
60
- const chainIdParam = searchParams?.get(`${prefix}ChainId`);
59
+ const currencyParam = searchParams.get(`${prefix}Currency`);
60
+ const chainIdParam = searchParams.get(`${prefix}ChainId`);
61
61
 
62
62
  // Determine if we should fetch token info
63
63
  const shouldFetchToken = Boolean(
@@ -0,0 +1 @@
1
+ export * from "./useNavigation";
@@ -0,0 +1,61 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ /**
4
+ * Framework-agnostic hook for reading URL search parameters
5
+ * Works with Next.js, Vite, and other React applications
6
+ */
7
+ export function useSearchParams() {
8
+ const [searchParams, setSearchParams] = useState<URLSearchParams>(() => {
9
+ if (typeof window !== "undefined") {
10
+ return new URLSearchParams(window.location.search);
11
+ }
12
+ return new URLSearchParams();
13
+ });
14
+
15
+ useEffect(() => {
16
+ if (typeof window !== "undefined") {
17
+ const params = new URLSearchParams(window.location.search);
18
+ setSearchParams(params);
19
+
20
+ // Listen for URL changes (for client-side routing)
21
+ const handlePopState = () => {
22
+ setSearchParams(new URLSearchParams(window.location.search));
23
+ };
24
+
25
+ window.addEventListener("popstate", handlePopState);
26
+ return () => window.removeEventListener("popstate", handlePopState);
27
+ }
28
+ }, []);
29
+
30
+ return searchParams;
31
+ }
32
+
33
+ /**
34
+ * Framework-agnostic navigation utility
35
+ * Works with Next.js, Vite, and other React applications
36
+ */
37
+ export function useRouter() {
38
+ const push = (url: string) => {
39
+ if (typeof window !== "undefined") {
40
+ // For client-side routing frameworks, we should use history.pushState
41
+ // This will work for most SPA frameworks
42
+ window.history.pushState({}, "", url);
43
+
44
+ // Dispatch a custom event that components can listen to
45
+ window.dispatchEvent(new PopStateEvent("popstate"));
46
+ }
47
+ };
48
+
49
+ return { push };
50
+ }
51
+
52
+ /**
53
+ * Direct utility function for getting search params without a hook
54
+ * Useful for server-side or one-time usage
55
+ */
56
+ export function getSearchParams(): URLSearchParams | null {
57
+ if (typeof window !== "undefined") {
58
+ return new URLSearchParams(window.location.search);
59
+ }
60
+ return null;
61
+ }
@@ -0,0 +1 @@
1
+ export * from "./hooks";