@b3dotfun/sdk 0.0.16 → 0.0.17-alpha.0
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.
- package/dist/cjs/anyspend/react/components/AnySpend.d.ts +5 -2
- package/dist/cjs/anyspend/react/components/AnySpend.js +264 -55
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +25 -29
- package/dist/cjs/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
- package/dist/cjs/anyspend/react/components/common/ConnectWalletPayment.js +49 -0
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +118 -0
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +71 -0
- package/dist/cjs/anyspend/react/components/common/OrderDetails.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +41 -60
- package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
- package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +42 -0
- package/dist/cjs/anyspend/react/components/common/OrderStatus.js +28 -5
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmountFiat.js +50 -0
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmountNew.js +63 -0
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +53 -15
- package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +1 -12
- package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +4 -12
- package/dist/cjs/anyspend/react/components/common/StepProgress.d.ts +11 -0
- package/dist/cjs/anyspend/react/components/common/StepProgress.js +22 -0
- package/dist/cjs/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
- package/dist/cjs/anyspend/react/components/common/TransferCryptoDetails.js +73 -0
- package/dist/cjs/anyspend/react/components/index.d.ts +3 -0
- package/dist/cjs/anyspend/react/components/index.js +7 -1
- package/dist/cjs/anyspend/react/components/webview/WebviewOnrampPayment.js +1 -10
- package/dist/cjs/anyspend/utils/format.d.ts +1 -0
- package/dist/cjs/anyspend/utils/format.js +23 -10
- package/dist/cjs/anyspend/utils/number.d.ts +7 -0
- package/dist/cjs/anyspend/utils/number.js +18 -0
- package/dist/esm/anyspend/react/components/AnySpend.d.ts +5 -2
- package/dist/esm/anyspend/react/components/AnySpend.js +266 -57
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +27 -31
- package/dist/esm/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
- package/dist/esm/anyspend/react/components/common/ConnectWalletPayment.js +46 -0
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +114 -0
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +67 -0
- package/dist/esm/anyspend/react/components/common/OrderDetails.d.ts +2 -0
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +43 -62
- package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
- package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +36 -0
- package/dist/esm/anyspend/react/components/common/OrderStatus.js +27 -4
- package/dist/esm/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
- package/dist/esm/anyspend/react/components/common/OrderTokenAmountFiat.js +47 -0
- package/dist/esm/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
- package/dist/esm/anyspend/react/components/common/OrderTokenAmountNew.js +60 -0
- package/dist/esm/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
- package/dist/esm/anyspend/react/components/common/PanelOnramp.js +54 -16
- package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +1 -12
- package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +7 -15
- package/dist/esm/anyspend/react/components/common/StepProgress.d.ts +11 -0
- package/dist/esm/anyspend/react/components/common/StepProgress.js +19 -0
- package/dist/esm/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
- package/dist/esm/anyspend/react/components/common/TransferCryptoDetails.js +70 -0
- package/dist/esm/anyspend/react/components/index.d.ts +3 -0
- package/dist/esm/anyspend/react/components/index.js +3 -0
- package/dist/esm/anyspend/react/components/webview/WebviewOnrampPayment.js +1 -10
- package/dist/esm/anyspend/utils/format.d.ts +1 -0
- package/dist/esm/anyspend/utils/format.js +23 -10
- package/dist/esm/anyspend/utils/number.d.ts +7 -0
- package/dist/esm/anyspend/utils/number.js +17 -0
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpend.d.ts +5 -2
- package/dist/types/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
- package/dist/types/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
- package/dist/types/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
- package/dist/types/anyspend/react/components/common/OrderDetails.d.ts +2 -0
- package/dist/types/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
- package/dist/types/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
- package/dist/types/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
- package/dist/types/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
- package/dist/types/anyspend/react/components/common/StepProgress.d.ts +11 -0
- package/dist/types/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
- package/dist/types/anyspend/react/components/index.d.ts +3 -0
- package/dist/types/anyspend/utils/format.d.ts +1 -0
- package/dist/types/anyspend/utils/number.d.ts +7 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpend.tsx +535 -177
- package/src/anyspend/react/components/AnySpendCustom.tsx +33 -38
- package/src/anyspend/react/components/common/ConnectWalletPayment.tsx +124 -0
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +227 -0
- package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +173 -0
- package/src/anyspend/react/components/common/OrderDetails.tsx +123 -250
- package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +156 -0
- package/src/anyspend/react/components/common/OrderStatus.tsx +47 -24
- package/src/anyspend/react/components/common/OrderTokenAmountFiat.tsx +147 -0
- package/src/anyspend/react/components/common/OrderTokenAmountNew.tsx +215 -0
- package/src/anyspend/react/components/common/PanelOnramp.tsx +215 -62
- package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +1 -14
- package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +42 -99
- package/src/anyspend/react/components/common/StepProgress.tsx +104 -0
- package/src/anyspend/react/components/common/TransferCryptoDetails.tsx +261 -0
- package/src/anyspend/react/components/index.ts +3 -0
- package/src/anyspend/react/components/webview/WebviewOnrampPayment.tsx +1 -11
- package/src/anyspend/utils/format.ts +24 -11
- package/src/anyspend/utils/number.ts +18 -0
- package/src/styles/index.css +30 -11
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ALL_CHAINS, capitalizeFirstLetter, getChainName } from "@b3dotfun/sdk/anyspend";
|
|
4
|
+
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
5
|
+
import { CopyToClipboard } from "@b3dotfun/sdk/global-account/react";
|
|
6
|
+
import { cn } from "@b3dotfun/sdk/shared/utils";
|
|
7
|
+
import centerTruncate from "@b3dotfun/sdk/shared/utils/centerTruncate";
|
|
8
|
+
import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
|
|
9
|
+
import { ChevronDown, Copy } from "lucide-react";
|
|
10
|
+
import { motion } from "motion/react";
|
|
11
|
+
import { memo, useState } from "react";
|
|
12
|
+
import { toast } from "sonner";
|
|
13
|
+
import { b3 } from "viem/chains";
|
|
14
|
+
|
|
15
|
+
type Order = components["schemas"]["Order"];
|
|
16
|
+
type Token = components["schemas"]["Token"];
|
|
17
|
+
type Tournament = components["schemas"]["Tournament"];
|
|
18
|
+
type NFT = components["schemas"]["NFT"];
|
|
19
|
+
|
|
20
|
+
interface OrderDetailsCollapsibleProps {
|
|
21
|
+
order: Order;
|
|
22
|
+
dstToken: Token;
|
|
23
|
+
tournament?: Tournament;
|
|
24
|
+
nft?: NFT;
|
|
25
|
+
recipientName?: string;
|
|
26
|
+
formattedExpectedDstAmount?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
showTotal?: boolean;
|
|
29
|
+
totalAmount?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const OrderDetailsCollapsible = memo(function OrderDetailsCollapsible({
|
|
33
|
+
order,
|
|
34
|
+
dstToken,
|
|
35
|
+
tournament,
|
|
36
|
+
nft,
|
|
37
|
+
recipientName,
|
|
38
|
+
formattedExpectedDstAmount,
|
|
39
|
+
className,
|
|
40
|
+
showTotal = false,
|
|
41
|
+
totalAmount,
|
|
42
|
+
}: OrderDetailsCollapsibleProps) {
|
|
43
|
+
const [showOrderDetails, setShowOrderDetails] = useState(true);
|
|
44
|
+
|
|
45
|
+
// Calculate expected amount if not provided
|
|
46
|
+
const expectedDstAmount =
|
|
47
|
+
order.type === "mint_nft" ||
|
|
48
|
+
order.type === "join_tournament" ||
|
|
49
|
+
order.type === "fund_tournament" ||
|
|
50
|
+
order.type === "custom"
|
|
51
|
+
? "0"
|
|
52
|
+
: order.payload.expectedDstAmount.toString();
|
|
53
|
+
|
|
54
|
+
const finalFormattedExpectedDstAmount =
|
|
55
|
+
formattedExpectedDstAmount || formatTokenAmount(BigInt(expectedDstAmount), dstToken.decimals);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className={cn("bg-as-surface-secondary border-as-border-secondary rounded-xl border px-4 py-2", className)}>
|
|
59
|
+
{showOrderDetails ? (
|
|
60
|
+
<motion.div
|
|
61
|
+
className="w-full"
|
|
62
|
+
initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
|
|
63
|
+
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
|
|
64
|
+
transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
|
|
65
|
+
>
|
|
66
|
+
<div className="flex w-full flex-col items-center gap-3 whitespace-nowrap py-2 text-sm">
|
|
67
|
+
{/* Recipient Section */}
|
|
68
|
+
<div className="flex w-full justify-between gap-4">
|
|
69
|
+
<div className="text-as-tertiarry">Recipient</div>
|
|
70
|
+
<div className="flex flex-col items-end gap-1">
|
|
71
|
+
{recipientName && <div className="text-as-primary font-semibold">{recipientName}</div>}
|
|
72
|
+
<CopyToClipboard
|
|
73
|
+
text={order.recipientAddress}
|
|
74
|
+
onCopy={() => {
|
|
75
|
+
toast.success("Copied recipient address to clipboard");
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<div className="text-as-primary flex items-center gap-2">
|
|
79
|
+
{centerTruncate(order.recipientAddress, 10)}
|
|
80
|
+
<Copy className="text-as-primary/50 hover:text-as-primary h-4 w-4 cursor-pointer transition-all duration-200" />
|
|
81
|
+
</div>
|
|
82
|
+
</CopyToClipboard>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="divider w-full" />
|
|
86
|
+
|
|
87
|
+
{/* Expected Amount/Action Section */}
|
|
88
|
+
<div className="flex w-full items-center justify-between gap-2">
|
|
89
|
+
<div className="text-as-tertiarry">
|
|
90
|
+
{order.type === "swap" || order.type === "mint_nft"
|
|
91
|
+
? "Expected to receive"
|
|
92
|
+
: order.type === "join_tournament"
|
|
93
|
+
? "Join tournament"
|
|
94
|
+
: order.type === "fund_tournament"
|
|
95
|
+
? "Fund tournament"
|
|
96
|
+
: order.type === "custom"
|
|
97
|
+
? order.metadata.action
|
|
98
|
+
? capitalizeFirstLetter(order.metadata.action)
|
|
99
|
+
: "Contract execution"
|
|
100
|
+
: ""}
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div className="flex items-end gap-2">
|
|
104
|
+
{order.type === "swap" ? (
|
|
105
|
+
`~${finalFormattedExpectedDstAmount} ${dstToken.symbol}`
|
|
106
|
+
) : order.type === "mint_nft" ? (
|
|
107
|
+
<div className="flex items-center gap-2">
|
|
108
|
+
<img src={nft?.imageUrl} alt={nft?.name || "NFT"} className="h-5 w-5" />
|
|
109
|
+
<div>{nft?.name || "NFT"}</div>
|
|
110
|
+
</div>
|
|
111
|
+
) : order.type === "join_tournament" || order.type === "fund_tournament" ? (
|
|
112
|
+
<div className="flex items-center gap-2">
|
|
113
|
+
<img src={tournament?.imageUrl} alt={tournament?.name || "Tournament"} className="h-5 w-5" />
|
|
114
|
+
<div>{tournament?.name || "Tournament"}</div>
|
|
115
|
+
</div>
|
|
116
|
+
) : null}
|
|
117
|
+
|
|
118
|
+
<div className="text-as-primary/50 flex items-center gap-2">
|
|
119
|
+
<span>on {order.dstChain !== b3.id && getChainName(order.dstChain)}</span>
|
|
120
|
+
<img
|
|
121
|
+
src={ALL_CHAINS[order.dstChain].logoUrl}
|
|
122
|
+
alt={getChainName(order.dstChain)}
|
|
123
|
+
className={cn(
|
|
124
|
+
"h-3",
|
|
125
|
+
order.dstChain !== b3.id && "w-3 rounded-full",
|
|
126
|
+
order.dstChain === b3.id && "h-4",
|
|
127
|
+
)}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div className="divider w-full" />
|
|
134
|
+
|
|
135
|
+
{/* Order ID / Total Section */}
|
|
136
|
+
<div className="flex w-full justify-between gap-4">
|
|
137
|
+
<div className="text-as-tertiarry">{showTotal ? "Total (included fee)" : "Order ID"}</div>
|
|
138
|
+
<div className="text-as-primary overflow-hidden text-ellipsis whitespace-nowrap">
|
|
139
|
+
{showTotal && totalAmount ? totalAmount : order.id}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</motion.div>
|
|
144
|
+
) : (
|
|
145
|
+
<div className="flex w-full items-center">
|
|
146
|
+
<div className="divider w-full" />
|
|
147
|
+
<button className="whitespace-nowrap text-sm" onClick={() => setShowOrderDetails(true)}>
|
|
148
|
+
Order Details
|
|
149
|
+
</button>
|
|
150
|
+
<ChevronDown className="text-as-primary mx-1 h-4 min-h-4 w-4 min-w-4" />
|
|
151
|
+
<div className="divider w-full" />
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
});
|
|
@@ -1,38 +1,61 @@
|
|
|
1
1
|
import { getStatusDisplay } from "@b3dotfun/sdk/anyspend";
|
|
2
2
|
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
3
|
-
import {
|
|
4
|
-
import { Check, Loader2 } from "lucide-react";
|
|
3
|
+
import { Check, X } from "lucide-react";
|
|
5
4
|
import { memo } from "react";
|
|
5
|
+
import { Step, StepProgress } from "./StepProgress";
|
|
6
6
|
|
|
7
7
|
export const OrderStatus = memo(function OrderStatus({ order }: { order: components["schemas"]["Order"] }) {
|
|
8
8
|
const isComplete = order.status === "executed";
|
|
9
|
-
const { text, status: displayStatus } = getStatusDisplay(order);
|
|
9
|
+
const { text, status: displayStatus, description } = getStatusDisplay(order);
|
|
10
|
+
|
|
11
|
+
console.log("OrderStatus", displayStatus);
|
|
12
|
+
console.log("OrderStatus", order);
|
|
13
|
+
|
|
14
|
+
const paymentSteps: Step[] = [
|
|
15
|
+
{
|
|
16
|
+
id: 1,
|
|
17
|
+
title: text,
|
|
18
|
+
description: description,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 2,
|
|
22
|
+
title: text,
|
|
23
|
+
description: description,
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if (order.status === "waiting_stripe_payment") {
|
|
28
|
+
return <StepProgress steps={paymentSteps} currentStepIndex={0} />;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (order.status === "relay") {
|
|
32
|
+
return <StepProgress steps={paymentSteps} currentStepIndex={1} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!isComplete && displayStatus !== "failure") {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
10
38
|
|
|
11
39
|
return (
|
|
12
40
|
<div className="flex items-center justify-center gap-2">
|
|
13
41
|
{isComplete ? (
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
</
|
|
21
|
-
) : displayStatus === "failure" ? (
|
|
22
|
-
<Badge variant="destructive" className="border-red-400/50 bg-red-400/20 px-4 py-1 text-base">
|
|
23
|
-
<div className="font-sf-rounded text-base font-semibold text-red-400/50">{text}</div>
|
|
24
|
-
</Badge>
|
|
42
|
+
<div className="flex flex-col items-center">
|
|
43
|
+
<div className={`bg-as-success-secondary relative flex h-10 w-10 items-center justify-center rounded-full`}>
|
|
44
|
+
<Check className="text-as-content-icon-success h-6 w-6" />
|
|
45
|
+
</div>
|
|
46
|
+
<h2 className="text-as-primary mt-4 text-xl font-semibold">{text}</h2>
|
|
47
|
+
<div className="text-as-tertiarry mt-1 text-center">{description}</div>
|
|
48
|
+
</div>
|
|
25
49
|
) : (
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</Badge>
|
|
50
|
+
<div className="flex flex-col items-center">
|
|
51
|
+
<div className="bg-as-error-secondary flex h-10 w-10 items-center justify-center rounded-full text-base">
|
|
52
|
+
<X className="text-as-content-icon-error h-5 w-5" />
|
|
53
|
+
</div>
|
|
54
|
+
<div className="font-sf-rounded text-as-content-primary mt-4 text-lg font-semibold">{text}</div>
|
|
55
|
+
<div className="text-as-tertiarry text-center" style={{ whiteSpace: "normal" }}>
|
|
56
|
+
{description}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
36
59
|
)}
|
|
37
60
|
</div>
|
|
38
61
|
);
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ChevronsUpDown } from "lucide-react";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { NumericFormat } from "react-number-format";
|
|
6
|
+
|
|
7
|
+
import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
|
|
8
|
+
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
9
|
+
import { cn } from "@b3dotfun/sdk/shared/utils";
|
|
10
|
+
import { TokenSelector } from "@reservoir0x/relay-kit-ui";
|
|
11
|
+
import { ChainTokenIcon } from "./ChainTokenIcon";
|
|
12
|
+
|
|
13
|
+
export function OrderTokenAmountFiat({
|
|
14
|
+
disabled,
|
|
15
|
+
inputValue,
|
|
16
|
+
onChangeInput,
|
|
17
|
+
context,
|
|
18
|
+
address,
|
|
19
|
+
chainId,
|
|
20
|
+
setChainId,
|
|
21
|
+
token,
|
|
22
|
+
setToken,
|
|
23
|
+
className,
|
|
24
|
+
}: {
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
inputValue: string;
|
|
27
|
+
onChangeInput: (value: string) => void;
|
|
28
|
+
context: "from" | "to";
|
|
29
|
+
address: string | undefined;
|
|
30
|
+
token: components["schemas"]["Token"];
|
|
31
|
+
setToken: (token: components["schemas"]["Token"]) => void;
|
|
32
|
+
chainId: number;
|
|
33
|
+
setChainId: (chainId: number) => void;
|
|
34
|
+
className?: string;
|
|
35
|
+
}) {
|
|
36
|
+
// Track previous token to detect changes
|
|
37
|
+
const prevTokenRef = useRef<string>(token.address);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
// Only trigger when token actually changes
|
|
41
|
+
if (prevTokenRef.current !== token.address) {
|
|
42
|
+
console.log(`Token changed from ${prevTokenRef.current} to ${token.address}`);
|
|
43
|
+
|
|
44
|
+
// For "from" context, reset to default value when token changes
|
|
45
|
+
if (context === "from") {
|
|
46
|
+
// Reset input to default for new token
|
|
47
|
+
onChangeInput("0.01");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Update ref to current token
|
|
51
|
+
prevTokenRef.current = token.address;
|
|
52
|
+
}
|
|
53
|
+
}, [token.address, chainId, context, onChangeInput]);
|
|
54
|
+
|
|
55
|
+
const handleTokenSelect = (newToken: any) => {
|
|
56
|
+
// Mark that we're about to change tokens
|
|
57
|
+
prevTokenRef.current = "changing"; // Temporary value to force effect
|
|
58
|
+
|
|
59
|
+
// Set the chain ID first
|
|
60
|
+
setChainId(newToken.chainId);
|
|
61
|
+
|
|
62
|
+
// Then set the new token
|
|
63
|
+
setToken({
|
|
64
|
+
address: newToken.address,
|
|
65
|
+
chainId: newToken.chainId, // Use the new chain ID
|
|
66
|
+
decimals: newToken.decimals,
|
|
67
|
+
metadata: { logoURI: newToken.logoURI },
|
|
68
|
+
name: newToken.name,
|
|
69
|
+
symbol: newToken.symbol,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// If this is the source token, reset the amount immediately
|
|
73
|
+
if (context === "from") {
|
|
74
|
+
onChangeInput("0.01");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Original token amount input design for other contexts
|
|
79
|
+
return (
|
|
80
|
+
<TokenSelector
|
|
81
|
+
address={address}
|
|
82
|
+
chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
83
|
+
context={context}
|
|
84
|
+
fromChainWalletVMSupported={true}
|
|
85
|
+
isValidAddress={true}
|
|
86
|
+
key={`selector-${context}-${token.address}-${chainId}`}
|
|
87
|
+
lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
88
|
+
multiWalletSupportEnabled={true}
|
|
89
|
+
onAnalyticEvent={undefined}
|
|
90
|
+
popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
|
|
91
|
+
setToken={handleTokenSelect}
|
|
92
|
+
supportedWalletVMs={["evm", "svm"]}
|
|
93
|
+
token={undefined}
|
|
94
|
+
trigger={
|
|
95
|
+
<div
|
|
96
|
+
className={cn(
|
|
97
|
+
"border-as-border-secondary bg-as-surface-primary flex cursor-pointer items-center justify-between rounded-xl border px-3 py-2",
|
|
98
|
+
className,
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
<div className="flex items-center gap-3">
|
|
102
|
+
{token.metadata?.logoURI ? (
|
|
103
|
+
<ChainTokenIcon
|
|
104
|
+
chainUrl={ALL_CHAINS[chainId]?.logoUrl}
|
|
105
|
+
tokenUrl={token.metadata.logoURI}
|
|
106
|
+
className="h-10 w-10"
|
|
107
|
+
/>
|
|
108
|
+
) : (
|
|
109
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600">
|
|
110
|
+
<span className="font-bold text-white">{token.symbol?.substring(0, 2) || "??"}</span>
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
<div>
|
|
114
|
+
<div className="text-as-primary font-semibold">{token.symbol}</div>
|
|
115
|
+
<div className="text-as-primary/50 text-sm">{ALL_CHAINS[chainId]?.name || "Unknown"}</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="flex items-center gap-2">
|
|
119
|
+
<span className="text-sm text-gray-600">≈</span>
|
|
120
|
+
<NumericFormat
|
|
121
|
+
key={`input-${token.address}-${chainId}`}
|
|
122
|
+
decimalSeparator="."
|
|
123
|
+
allowedDecimalSeparators={[","]}
|
|
124
|
+
thousandSeparator
|
|
125
|
+
inputMode="decimal"
|
|
126
|
+
autoComplete="off"
|
|
127
|
+
autoCorrect="off"
|
|
128
|
+
type="text"
|
|
129
|
+
placeholder="0.00"
|
|
130
|
+
minLength={1}
|
|
131
|
+
maxLength={20}
|
|
132
|
+
spellCheck="false"
|
|
133
|
+
className="text-as-primary bg-as-surface-primary w-[100px]"
|
|
134
|
+
pattern="^[0-9]*[.,]?[0-9]*$"
|
|
135
|
+
disabled={disabled}
|
|
136
|
+
value={inputValue}
|
|
137
|
+
allowNegative={false}
|
|
138
|
+
aria-disabled
|
|
139
|
+
readOnly
|
|
140
|
+
/>
|
|
141
|
+
<ChevronsUpDown className="h-4 w-4 cursor-pointer text-gray-400" />
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ChevronsUpDown } from "lucide-react";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { NumericFormat } from "react-number-format";
|
|
6
|
+
|
|
7
|
+
import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
|
|
8
|
+
import { Button } from "@b3dotfun/sdk/global-account/react";
|
|
9
|
+
import { cn } from "@b3dotfun/sdk/shared/utils";
|
|
10
|
+
import { TokenSelector } from "@reservoir0x/relay-kit-ui";
|
|
11
|
+
import { ChainTokenIcon } from "./ChainTokenIcon";
|
|
12
|
+
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
13
|
+
|
|
14
|
+
export function OrderTokenAmountFiat({
|
|
15
|
+
disabled,
|
|
16
|
+
inputValue,
|
|
17
|
+
onChangeInput,
|
|
18
|
+
context,
|
|
19
|
+
address,
|
|
20
|
+
chainId,
|
|
21
|
+
setChainId,
|
|
22
|
+
token,
|
|
23
|
+
setToken,
|
|
24
|
+
hideTokenSelect = false,
|
|
25
|
+
canEditAmount = true,
|
|
26
|
+
className,
|
|
27
|
+
showAsReceiveAmount = false,
|
|
28
|
+
}: {
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
inputValue: string;
|
|
31
|
+
onChangeInput: (value: string) => void;
|
|
32
|
+
context: "from" | "to";
|
|
33
|
+
address: string | undefined;
|
|
34
|
+
token: components["schemas"]["Token"];
|
|
35
|
+
setToken: (token: components["schemas"]["Token"]) => void;
|
|
36
|
+
chainId: number;
|
|
37
|
+
setChainId: (chainId: number) => void;
|
|
38
|
+
hideTokenSelect?: boolean;
|
|
39
|
+
canEditAmount?: boolean;
|
|
40
|
+
className?: string;
|
|
41
|
+
showAsReceiveAmount?: boolean;
|
|
42
|
+
}) {
|
|
43
|
+
// Track previous token to detect changes
|
|
44
|
+
const prevTokenRef = useRef<string>(token.address);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
// Only trigger when token actually changes
|
|
48
|
+
if (prevTokenRef.current !== token.address) {
|
|
49
|
+
console.log(`Token changed from ${prevTokenRef.current} to ${token.address}`);
|
|
50
|
+
|
|
51
|
+
// For "from" context, reset to default value when token changes
|
|
52
|
+
if (context === "from") {
|
|
53
|
+
// Reset input to default for new token
|
|
54
|
+
onChangeInput("0.01");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Update ref to current token
|
|
58
|
+
prevTokenRef.current = token.address;
|
|
59
|
+
}
|
|
60
|
+
}, [token.address, chainId, context, onChangeInput]);
|
|
61
|
+
|
|
62
|
+
const handleTokenSelect = (newToken: any) => {
|
|
63
|
+
// Mark that we're about to change tokens
|
|
64
|
+
prevTokenRef.current = "changing"; // Temporary value to force effect
|
|
65
|
+
|
|
66
|
+
// Set the chain ID first
|
|
67
|
+
setChainId(newToken.chainId);
|
|
68
|
+
|
|
69
|
+
// Then set the new token
|
|
70
|
+
setToken({
|
|
71
|
+
address: newToken.address,
|
|
72
|
+
chainId: newToken.chainId, // Use the new chain ID
|
|
73
|
+
decimals: newToken.decimals,
|
|
74
|
+
metadata: { logoURI: newToken.logoURI },
|
|
75
|
+
name: newToken.name,
|
|
76
|
+
symbol: newToken.symbol,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// If this is the source token, reset the amount immediately
|
|
80
|
+
if (context === "from") {
|
|
81
|
+
onChangeInput("0.01");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Calculate formatted amount for display
|
|
86
|
+
const formatAmount = (amount: string) => {
|
|
87
|
+
const numAmount = parseFloat(amount) || 0;
|
|
88
|
+
return numAmount.toLocaleString("en-US", {
|
|
89
|
+
minimumFractionDigits: 2,
|
|
90
|
+
maximumFractionDigits: 2,
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (showAsReceiveAmount) {
|
|
95
|
+
// Design-matched token display for receive amounts (like in PanelOnramp)
|
|
96
|
+
return (
|
|
97
|
+
<div className={cn("flex items-center justify-between rounded-xl bg-gray-50 p-4", className)}>
|
|
98
|
+
<div className="flex items-center gap-3">
|
|
99
|
+
{token.metadata?.logoURI ? (
|
|
100
|
+
<ChainTokenIcon
|
|
101
|
+
chainUrl={ALL_CHAINS[chainId]?.logoUrl}
|
|
102
|
+
tokenUrl={token.metadata.logoURI}
|
|
103
|
+
className="h-10 w-10"
|
|
104
|
+
/>
|
|
105
|
+
) : (
|
|
106
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600">
|
|
107
|
+
<span className="font-bold text-white">{token.symbol?.substring(0, 2) || "??"}</span>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
<div>
|
|
111
|
+
<div className="text-base font-semibold text-gray-900">{token.symbol}</div>
|
|
112
|
+
<div className="text-sm text-gray-600">{ALL_CHAINS[chainId]?.name || "Unknown"}</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex items-center gap-2">
|
|
116
|
+
<span className="text-sm text-gray-600">≈</span>
|
|
117
|
+
<span className="text-lg font-semibold text-gray-900">{formatAmount(inputValue)}</span>
|
|
118
|
+
{!hideTokenSelect && (
|
|
119
|
+
<TokenSelector
|
|
120
|
+
address={address}
|
|
121
|
+
chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
122
|
+
context={context}
|
|
123
|
+
fromChainWalletVMSupported={true}
|
|
124
|
+
isValidAddress={true}
|
|
125
|
+
key={`selector-${context}-${token.address}-${chainId}`}
|
|
126
|
+
lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
127
|
+
multiWalletSupportEnabled={true}
|
|
128
|
+
onAnalyticEvent={undefined}
|
|
129
|
+
popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
|
|
130
|
+
setToken={handleTokenSelect}
|
|
131
|
+
supportedWalletVMs={["evm", "svm"]}
|
|
132
|
+
token={undefined}
|
|
133
|
+
trigger={<ChevronsUpDown className="h-4 w-4 cursor-pointer text-gray-400" />}
|
|
134
|
+
/>
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Original token amount input design for other contexts
|
|
142
|
+
return (
|
|
143
|
+
<div
|
|
144
|
+
className={cn("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className)}
|
|
145
|
+
key={`${context}-${token.address}-${chainId}`}
|
|
146
|
+
>
|
|
147
|
+
<div className="flex items-center justify-between gap-3">
|
|
148
|
+
{!canEditAmount ? (
|
|
149
|
+
<h2 className="text-3xl font-medium text-white">{inputValue || "--"}</h2>
|
|
150
|
+
) : (
|
|
151
|
+
<NumericFormat
|
|
152
|
+
key={`input-${token.address}-${chainId}`}
|
|
153
|
+
decimalSeparator="."
|
|
154
|
+
allowedDecimalSeparators={[","]}
|
|
155
|
+
thousandSeparator
|
|
156
|
+
inputMode="decimal"
|
|
157
|
+
autoComplete="off"
|
|
158
|
+
autoCorrect="off"
|
|
159
|
+
type="text"
|
|
160
|
+
placeholder="0.00"
|
|
161
|
+
minLength={1}
|
|
162
|
+
maxLength={30}
|
|
163
|
+
spellCheck="false"
|
|
164
|
+
className="placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]"
|
|
165
|
+
pattern="^[0-9]*[.,]?[0-9]*$"
|
|
166
|
+
disabled={disabled}
|
|
167
|
+
value={inputValue}
|
|
168
|
+
allowNegative={false}
|
|
169
|
+
onChange={e => onChangeInput(e.currentTarget.value)}
|
|
170
|
+
/>
|
|
171
|
+
)}
|
|
172
|
+
|
|
173
|
+
{!hideTokenSelect && (
|
|
174
|
+
<TokenSelector
|
|
175
|
+
address={address}
|
|
176
|
+
chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
177
|
+
context={context}
|
|
178
|
+
fromChainWalletVMSupported={true}
|
|
179
|
+
isValidAddress={true}
|
|
180
|
+
key={`selector-${context}-${token.address}-${chainId}`}
|
|
181
|
+
lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
|
|
182
|
+
multiWalletSupportEnabled={true}
|
|
183
|
+
onAnalyticEvent={undefined}
|
|
184
|
+
popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
|
|
185
|
+
setToken={handleTokenSelect}
|
|
186
|
+
supportedWalletVMs={["evm", "svm"]}
|
|
187
|
+
token={undefined}
|
|
188
|
+
trigger={
|
|
189
|
+
<Button
|
|
190
|
+
variant="outline"
|
|
191
|
+
role="combobox"
|
|
192
|
+
className="bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center"
|
|
193
|
+
>
|
|
194
|
+
{token.metadata?.logoURI ? (
|
|
195
|
+
<ChainTokenIcon
|
|
196
|
+
chainUrl={ALL_CHAINS[chainId]?.logoUrl}
|
|
197
|
+
tokenUrl={token.metadata.logoURI}
|
|
198
|
+
className="h-8 min-h-8 w-8 min-w-8"
|
|
199
|
+
/>
|
|
200
|
+
) : (
|
|
201
|
+
<div className="h-8 w-8 rounded-full bg-gray-700" />
|
|
202
|
+
)}
|
|
203
|
+
<div className="flex flex-col items-start gap-0">
|
|
204
|
+
<div className="text-as-primary font-semibold">{token.symbol}</div>
|
|
205
|
+
<div className="text-as-primary/70 text-xs">{ALL_CHAINS[chainId]?.name}</div>
|
|
206
|
+
</div>
|
|
207
|
+
<ChevronsUpDown className="h-4 w-4 shrink-0 opacity-70" />
|
|
208
|
+
</Button>
|
|
209
|
+
}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|