@ensofinance/checkout-widget 0.1.6 → 0.1.8
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/checkout-widget.es.js +25523 -24215
- package/dist/checkout-widget.es.js.map +1 -1
- package/dist/checkout-widget.umd.js +64 -59
- package/dist/checkout-widget.umd.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/package.json +1 -1
- package/src/assets/providers/alchemypay.svg +21 -0
- package/src/assets/providers/banxa.svg +21 -0
- package/src/assets/providers/binanceconnect.svg +14 -0
- package/src/assets/providers/kryptonim.svg +6 -0
- package/src/assets/providers/mercuryo.svg +21 -0
- package/src/assets/providers/moonpay.svg +14 -0
- package/src/assets/providers/stripe.svg +16 -0
- package/src/assets/providers/swapped.svg +1 -0
- package/src/assets/providers/topper.svg +14 -0
- package/src/assets/providers/transak.svg +21 -0
- package/src/assets/providers/unlimit.svg +21 -0
- package/src/components/AmountInput.tsx +41 -25
- package/src/components/ChakraProvider.tsx +36 -13
- package/src/components/Checkout.tsx +7 -1
- package/src/components/CurrencySwapDisplay.tsx +59 -22
- package/src/components/DepositProcessing.tsx +1 -1
- package/src/components/ExchangeConfirmSecurity.tsx +1 -1
- package/src/components/QuoteParameters.tsx +1 -1
- package/src/components/TransactionDetailRow.tsx +2 -2
- package/src/components/cards/ExchangeCard.tsx +1 -1
- package/src/components/cards/OptionCard.tsx +2 -1
- package/src/components/cards/WalletCard.tsx +1 -1
- package/src/components/modal.tsx +3 -3
- package/src/components/steps/CardBuyFlow/CardBuyFlow.tsx +412 -0
- package/src/components/steps/CardBuyFlow/ChooseAmountStep.tsx +352 -0
- package/src/components/steps/CardBuyFlow/OpenWidgetStep.tsx +193 -0
- package/src/components/steps/ExchangeFlow.tsx +254 -1416
- package/src/components/steps/FlowSelector.tsx +117 -60
- package/src/components/steps/SmartAccountFlow.tsx +372 -0
- package/src/components/steps/WalletFlow/WalletAmountStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletConfirmStep.tsx +92 -51
- package/src/components/steps/WalletFlow/WalletFlow.tsx +17 -16
- package/src/components/steps/WalletFlow/WalletQuoteStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletTokenStep.tsx +6 -4
- package/src/components/steps/shared/ChooseAmountStep.tsx +325 -0
- package/src/components/steps/shared/SignUserOpStep.tsx +117 -0
- package/src/components/steps/shared/TrackUserOpStep.tsx +625 -0
- package/src/components/steps/shared/exchangeIntegration.ts +19 -0
- package/src/components/steps/shared/types.ts +22 -0
- package/src/components/ui/index.tsx +23 -6
- package/src/components/ui/toaster.tsx +2 -1
- package/src/components/ui/transitions.tsx +16 -0
- package/src/types/index.ts +99 -0
- package/src/util/constants.tsx +27 -0
- package/src/util/enso-hooks.tsx +75 -61
- package/src/util/meld-hooks.tsx +533 -0
- package/src/assets/usdc.webp +0 -0
- package/src/assets/usdt.webp +0 -0
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
SupportedChainId,
|
|
26
26
|
} from "@/util/constants";
|
|
27
27
|
import { useMemo } from "react";
|
|
28
|
-
import { EXCHANGE_ICON_BY_TYPE } from "@/components/steps/
|
|
28
|
+
import { EXCHANGE_ICON_BY_TYPE } from "@/components/steps/shared/exchangeIntegration";
|
|
29
29
|
|
|
30
30
|
export const ERROR_MSG =
|
|
31
31
|
"Swap not found for a required underlying of defi route, please make sure your amount is within an acceptable range";
|
|
@@ -12,7 +12,7 @@ export const DetailRowContainer = chakra("div", {
|
|
|
12
12
|
alignItems: "center",
|
|
13
13
|
h: "36px",
|
|
14
14
|
p: "8px 12px",
|
|
15
|
-
borderRadius: "
|
|
15
|
+
borderRadius: "card",
|
|
16
16
|
bg: "bg",
|
|
17
17
|
w: "100%",
|
|
18
18
|
},
|
|
@@ -57,7 +57,7 @@ export const TransactionDetailRow = () => {
|
|
|
57
57
|
<Box
|
|
58
58
|
width="100%"
|
|
59
59
|
bg="bg.subtle"
|
|
60
|
-
borderRadius="
|
|
60
|
+
borderRadius="card"
|
|
61
61
|
padding="12px"
|
|
62
62
|
transition="all 0.2s"
|
|
63
63
|
>
|
|
@@ -32,7 +32,7 @@ const ExchangeCard: FC<IProps> = ({
|
|
|
32
32
|
),
|
|
33
33
|
low: <Tag>Low Balance</Tag>,
|
|
34
34
|
connected: (
|
|
35
|
-
<Icon as={CheckIcon} color="
|
|
35
|
+
<Icon as={CheckIcon} color="success" width={"16px"} height={"16px"} />
|
|
36
36
|
),
|
|
37
37
|
none: null,
|
|
38
38
|
};
|
|
@@ -39,6 +39,7 @@ const OptionCard = ({
|
|
|
39
39
|
<Image
|
|
40
40
|
w={"32px"}
|
|
41
41
|
h={"32px"}
|
|
42
|
+
objectFit={"contain"}
|
|
42
43
|
borderRadius={"full"}
|
|
43
44
|
position={"relative"}
|
|
44
45
|
zIndex={index}
|
|
@@ -49,7 +50,7 @@ const OptionCard = ({
|
|
|
49
50
|
</Box>
|
|
50
51
|
<Icon
|
|
51
52
|
as={ChevronRight}
|
|
52
|
-
color="
|
|
53
|
+
color="fg.muted"
|
|
53
54
|
width={"16px"}
|
|
54
55
|
height={"16px"}
|
|
55
56
|
/>
|
package/src/components/modal.tsx
CHANGED
|
@@ -35,11 +35,11 @@ export const ModalWrapper = chakra("div", {
|
|
|
35
35
|
maxWidth: "374px",
|
|
36
36
|
justifyContent: "center",
|
|
37
37
|
alignItems: "center",
|
|
38
|
-
borderRadius: "
|
|
38
|
+
borderRadius: "card",
|
|
39
39
|
border: "1px solid",
|
|
40
|
-
borderColor: "
|
|
40
|
+
borderColor: "border",
|
|
41
41
|
padding: "16px",
|
|
42
|
-
backgroundColor: "
|
|
42
|
+
backgroundColor: "bg",
|
|
43
43
|
},
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { Center, Box, Icon, Text } from "@chakra-ui/react";
|
|
2
|
+
import { ChevronLeft, X, XCircle } from "lucide-react";
|
|
3
|
+
import {
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
useCallback,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { useAccount } from "wagmi";
|
|
12
|
+
import { BodyWrapper, HeaderTitle, HeaderWrapper } from "../../ui/styled";
|
|
13
|
+
import { IconButton } from "../../ui";
|
|
14
|
+
import { CheckoutContext } from "../../Checkout";
|
|
15
|
+
import Modal from "../../modal";
|
|
16
|
+
import { useAppStore } from "@/store";
|
|
17
|
+
import { denormalizeValue } from "@/util";
|
|
18
|
+
import { getUsdcAddress } from "@/util/constants";
|
|
19
|
+
import { useSmartAccountAddress } from "@/util/enso-hooks";
|
|
20
|
+
import {
|
|
21
|
+
useMeldQuotes,
|
|
22
|
+
useMeldCardBuyFlow,
|
|
23
|
+
useCountryCode,
|
|
24
|
+
getMeldDestinationCurrencyCode,
|
|
25
|
+
isMeldCardBuySupportedChain,
|
|
26
|
+
useMeldSupportedCrypto,
|
|
27
|
+
useIncomingUsdcTransferDetector,
|
|
28
|
+
} from "@/util/meld-hooks";
|
|
29
|
+
import type { MeldQuote } from "@/types";
|
|
30
|
+
|
|
31
|
+
import ChooseAmountStep from "./ChooseAmountStep";
|
|
32
|
+
import OpenWidgetStep from "./OpenWidgetStep";
|
|
33
|
+
import { AnimatedStep } from "../../ui/transitions";
|
|
34
|
+
|
|
35
|
+
interface CardBuyFlowProps {
|
|
36
|
+
setFlow: (flow: string) => void;
|
|
37
|
+
initialStep?: CardBuyStep;
|
|
38
|
+
onCardBuyDepositDetected: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Card buy flow steps
|
|
42
|
+
export enum CardBuyStep {
|
|
43
|
+
ChooseAmount = 0,
|
|
44
|
+
OpenWidget = 1,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const STEP_TITLES: Record<CardBuyStep, string> = {
|
|
48
|
+
[CardBuyStep.ChooseAmount]: "Buy with Card",
|
|
49
|
+
[CardBuyStep.OpenWidget]: "Complete Purchase",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const cardPreviousStep: Partial<Record<CardBuyStep, CardBuyStep>> = {
|
|
53
|
+
[CardBuyStep.OpenWidget]: CardBuyStep.ChooseAmount,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const CardBuyFlow = ({
|
|
57
|
+
setFlow,
|
|
58
|
+
initialStep = CardBuyStep.ChooseAmount,
|
|
59
|
+
onCardBuyDepositDetected,
|
|
60
|
+
}: CardBuyFlowProps) => {
|
|
61
|
+
const { handleClose } = useContext(CheckoutContext);
|
|
62
|
+
const { address } = useAccount();
|
|
63
|
+
|
|
64
|
+
// Store
|
|
65
|
+
const chainIdOut = useAppStore((s) => s.chainIdOut);
|
|
66
|
+
const setAmountIn = useAppStore((s) => s.setAmountIn);
|
|
67
|
+
const setTokenIn = useAppStore((s) => s.setTokenIn);
|
|
68
|
+
const setChainIdIn = useAppStore((s) => s.setChainIdIn);
|
|
69
|
+
const setSelectedIntegration = useAppStore((s) => s.setSelectedIntegration);
|
|
70
|
+
const handoffTriggeredRef = useRef(false);
|
|
71
|
+
|
|
72
|
+
// Set integration on mount so useRouteData picks "checkout" strategy,
|
|
73
|
+
// and clean up on unmount (same pattern as ExchangeFlow).
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
setSelectedIntegration({ id: "meld", type: "meld", name: "Card Buy" });
|
|
76
|
+
return () => setSelectedIntegration(null);
|
|
77
|
+
}, [setSelectedIntegration]);
|
|
78
|
+
|
|
79
|
+
// Local state
|
|
80
|
+
const [currentStep, setCurrentStep] = useState<CardBuyStep>(initialStep);
|
|
81
|
+
const [fiatCurrency, setFiatCurrency] = useState<"USD" | "EUR">("USD");
|
|
82
|
+
const [fiatAmount, setFiatAmount] = useState<string>("100");
|
|
83
|
+
const [selectedQuote, setSelectedQuote] = useState<MeldQuote | null>(null);
|
|
84
|
+
const [error, setError] = useState<string | null>(null);
|
|
85
|
+
|
|
86
|
+
// Smart account address - use recipient override if provided
|
|
87
|
+
const { smartAccountAddress } = useSmartAccountAddress(chainIdOut);
|
|
88
|
+
|
|
89
|
+
// Country code detection
|
|
90
|
+
const { countryCode, isLoading: countryCodeLoading } = useCountryCode();
|
|
91
|
+
const { data: supportedCryptos = [], isLoading: supportedCryptosLoading } =
|
|
92
|
+
useMeldSupportedCrypto({
|
|
93
|
+
countryCode,
|
|
94
|
+
enabled: !!chainIdOut,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const destinationSymbol = "USDC";
|
|
98
|
+
|
|
99
|
+
const destinationCryptoCode = useMemo(() => {
|
|
100
|
+
if (!chainIdOut) return null;
|
|
101
|
+
return getMeldDestinationCurrencyCode({
|
|
102
|
+
symbol: destinationSymbol,
|
|
103
|
+
chainId: chainIdOut,
|
|
104
|
+
supportedCryptos,
|
|
105
|
+
});
|
|
106
|
+
}, [destinationSymbol, chainIdOut, supportedCryptos]);
|
|
107
|
+
|
|
108
|
+
const isCardBuySupported =
|
|
109
|
+
!!chainIdOut &&
|
|
110
|
+
isMeldCardBuySupportedChain(chainIdOut, supportedCryptos);
|
|
111
|
+
|
|
112
|
+
// Fetch quotes
|
|
113
|
+
const {
|
|
114
|
+
data: quotes,
|
|
115
|
+
isLoading: quotesLoading,
|
|
116
|
+
error: quotesError,
|
|
117
|
+
} = useMeldQuotes({
|
|
118
|
+
sourceCurrency: fiatCurrency,
|
|
119
|
+
destinationCurrency: destinationCryptoCode || "",
|
|
120
|
+
amount: parseFloat(fiatAmount) || 0,
|
|
121
|
+
countryCode: countryCode || "",
|
|
122
|
+
walletAddress: smartAccountAddress || "",
|
|
123
|
+
enabled:
|
|
124
|
+
!!countryCode &&
|
|
125
|
+
!!destinationCryptoCode &&
|
|
126
|
+
parseFloat(fiatAmount) > 0 &&
|
|
127
|
+
!!smartAccountAddress,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Select best quote automatically
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (!quotes || quotes.length === 0) {
|
|
133
|
+
if (selectedQuote) setSelectedQuote(null);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Default to best quote
|
|
138
|
+
if (!selectedQuote) {
|
|
139
|
+
setSelectedQuote(quotes[0]);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Keep the same provider selected if still available, but refresh the
|
|
144
|
+
// quote object (rates/fees can update on refetch).
|
|
145
|
+
const matching = quotes.find(
|
|
146
|
+
(q) => q.serviceProvider === selectedQuote.serviceProvider,
|
|
147
|
+
);
|
|
148
|
+
if (!matching) {
|
|
149
|
+
setSelectedQuote(quotes[0]);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const didChange =
|
|
154
|
+
matching.destinationAmount !== selectedQuote.destinationAmount ||
|
|
155
|
+
matching.totalFee !== selectedQuote.totalFee ||
|
|
156
|
+
matching.exchangeRate !== selectedQuote.exchangeRate;
|
|
157
|
+
if (didChange) setSelectedQuote(matching);
|
|
158
|
+
}, [quotes, selectedQuote]);
|
|
159
|
+
|
|
160
|
+
// MELD flow state
|
|
161
|
+
const meldFlow = useMeldCardBuyFlow();
|
|
162
|
+
|
|
163
|
+
const shouldTrackIncomingTransfer =
|
|
164
|
+
currentStep === CardBuyStep.OpenWidget &&
|
|
165
|
+
!!chainIdOut &&
|
|
166
|
+
!!smartAccountAddress;
|
|
167
|
+
const {
|
|
168
|
+
detectedAmount,
|
|
169
|
+
isDetected,
|
|
170
|
+
reset: resetIncomingDetector,
|
|
171
|
+
} = useIncomingUsdcTransferDetector({
|
|
172
|
+
chainId: chainIdOut,
|
|
173
|
+
walletAddress: smartAccountAddress,
|
|
174
|
+
enabled: shouldTrackIncomingTransfer,
|
|
175
|
+
thresholdUsdc: "0.01",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const resetToChooseAmount = useCallback(
|
|
179
|
+
(clearQuote = false) => {
|
|
180
|
+
meldFlow.reset();
|
|
181
|
+
resetIncomingDetector();
|
|
182
|
+
handoffTriggeredRef.current = false;
|
|
183
|
+
setError(null);
|
|
184
|
+
if (clearQuote) {
|
|
185
|
+
setSelectedQuote(null);
|
|
186
|
+
}
|
|
187
|
+
setCurrentStep(CardBuyStep.ChooseAmount);
|
|
188
|
+
},
|
|
189
|
+
[meldFlow, resetIncomingDetector],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Navigation
|
|
193
|
+
const goBack = useCallback(() => {
|
|
194
|
+
if (currentStep === CardBuyStep.ChooseAmount) {
|
|
195
|
+
setFlow("");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const previousStep = cardPreviousStep[currentStep];
|
|
199
|
+
if (previousStep !== undefined) {
|
|
200
|
+
resetToChooseAmount(false);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
setFlow("");
|
|
204
|
+
}, [currentStep, setFlow, resetToChooseAmount]);
|
|
205
|
+
|
|
206
|
+
const goToOpenWidget = useCallback(() => {
|
|
207
|
+
if (!selectedQuote) return;
|
|
208
|
+
setError(null);
|
|
209
|
+
handoffTriggeredRef.current = false;
|
|
210
|
+
resetIncomingDetector();
|
|
211
|
+
setCurrentStep(CardBuyStep.OpenWidget);
|
|
212
|
+
}, [selectedQuote, resetIncomingDetector]);
|
|
213
|
+
|
|
214
|
+
// Start MELD session
|
|
215
|
+
const handleStartSession = useCallback(async () => {
|
|
216
|
+
if (
|
|
217
|
+
!selectedQuote ||
|
|
218
|
+
!smartAccountAddress ||
|
|
219
|
+
!destinationCryptoCode ||
|
|
220
|
+
!countryCode
|
|
221
|
+
)
|
|
222
|
+
return;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await meldFlow.startSession({
|
|
226
|
+
countryCode: countryCode,
|
|
227
|
+
sourceCurrencyCode: fiatCurrency,
|
|
228
|
+
destinationCurrencyCode: destinationCryptoCode,
|
|
229
|
+
sourceAmount: selectedQuote.sourceAmount,
|
|
230
|
+
walletAddress: smartAccountAddress,
|
|
231
|
+
serviceProvider: selectedQuote.serviceProvider,
|
|
232
|
+
paymentMethodType: "CREDIT_DEBIT_CARD",
|
|
233
|
+
externalCustomerId: address,
|
|
234
|
+
externalSessionId: `enso-${Date.now()}`,
|
|
235
|
+
});
|
|
236
|
+
} catch (err: any) {
|
|
237
|
+
console.error(err);
|
|
238
|
+
setError(err.message || "Failed to create session");
|
|
239
|
+
}
|
|
240
|
+
}, [
|
|
241
|
+
selectedQuote,
|
|
242
|
+
smartAccountAddress,
|
|
243
|
+
destinationCryptoCode,
|
|
244
|
+
countryCode,
|
|
245
|
+
fiatCurrency,
|
|
246
|
+
address,
|
|
247
|
+
meldFlow,
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
const handleOpenWidget = useCallback(() => {
|
|
251
|
+
meldFlow.openWidget();
|
|
252
|
+
}, [meldFlow]);
|
|
253
|
+
|
|
254
|
+
// Funds arrival is the source of truth for handing off into Smart Account flow.
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
if (currentStep !== CardBuyStep.OpenWidget) return;
|
|
257
|
+
if (!isDetected || !detectedAmount || !chainIdOut) return;
|
|
258
|
+
if (handoffTriggeredRef.current) return;
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
handoffTriggeredRef.current = true;
|
|
262
|
+
setError(null);
|
|
263
|
+
resetIncomingDetector();
|
|
264
|
+
|
|
265
|
+
// Store the detected amount and token before switching flow
|
|
266
|
+
setAmountIn(denormalizeValue(detectedAmount, 6));
|
|
267
|
+
setTokenIn(getUsdcAddress(chainIdOut));
|
|
268
|
+
setChainIdIn(chainIdOut);
|
|
269
|
+
|
|
270
|
+
onCardBuyDepositDetected();
|
|
271
|
+
} catch (err: any) {
|
|
272
|
+
handoffTriggeredRef.current = false;
|
|
273
|
+
setError(
|
|
274
|
+
err.message || "Failed to redirect to Smart Account quote",
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}, [
|
|
278
|
+
currentStep,
|
|
279
|
+
isDetected,
|
|
280
|
+
detectedAmount,
|
|
281
|
+
chainIdOut,
|
|
282
|
+
setAmountIn,
|
|
283
|
+
setTokenIn,
|
|
284
|
+
setChainIdIn,
|
|
285
|
+
onCardBuyDepositDetected,
|
|
286
|
+
resetIncomingDetector,
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
// Check if chain is supported
|
|
290
|
+
if (!chainIdOut || (!supportedCryptosLoading && !isCardBuySupported)) {
|
|
291
|
+
return (
|
|
292
|
+
<>
|
|
293
|
+
<Modal.Header>
|
|
294
|
+
<HeaderWrapper>
|
|
295
|
+
<IconButton onClick={() => setFlow("")} width="40px">
|
|
296
|
+
<Icon
|
|
297
|
+
as={ChevronLeft}
|
|
298
|
+
color="fg.muted"
|
|
299
|
+
width="16px"
|
|
300
|
+
height="16px"
|
|
301
|
+
/>
|
|
302
|
+
</IconButton>
|
|
303
|
+
<Box>
|
|
304
|
+
<HeaderTitle>Buy with Card</HeaderTitle>
|
|
305
|
+
</Box>
|
|
306
|
+
{handleClose && (
|
|
307
|
+
<IconButton onClick={handleClose} width="40px">
|
|
308
|
+
<Icon
|
|
309
|
+
as={X}
|
|
310
|
+
color="fg.muted"
|
|
311
|
+
width="16px"
|
|
312
|
+
height="16px"
|
|
313
|
+
/>
|
|
314
|
+
</IconButton>
|
|
315
|
+
)}
|
|
316
|
+
</HeaderWrapper>
|
|
317
|
+
</Modal.Header>
|
|
318
|
+
<Modal.Body>
|
|
319
|
+
<BodyWrapper>
|
|
320
|
+
<Center py={8} flexDirection="column" gap={4}>
|
|
321
|
+
<Icon as={XCircle} color="error" boxSize={12} />
|
|
322
|
+
<Text color="fg.muted" textAlign="center">
|
|
323
|
+
Card purchases are not supported for this chain.
|
|
324
|
+
</Text>
|
|
325
|
+
</Center>
|
|
326
|
+
</BodyWrapper>
|
|
327
|
+
</Modal.Body>
|
|
328
|
+
</>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Render step content
|
|
333
|
+
const renderStepContent = () => {
|
|
334
|
+
switch (currentStep) {
|
|
335
|
+
case CardBuyStep.ChooseAmount:
|
|
336
|
+
return (
|
|
337
|
+
<ChooseAmountStep
|
|
338
|
+
fiatCurrency={fiatCurrency}
|
|
339
|
+
setFiatCurrency={setFiatCurrency}
|
|
340
|
+
fiatAmount={fiatAmount}
|
|
341
|
+
setFiatAmount={setFiatAmount}
|
|
342
|
+
quotes={quotes}
|
|
343
|
+
quotesLoading={quotesLoading || countryCodeLoading}
|
|
344
|
+
quotesError={
|
|
345
|
+
quotesError
|
|
346
|
+
? quotesError.message || String(quotesError)
|
|
347
|
+
: null
|
|
348
|
+
}
|
|
349
|
+
selectedQuote={selectedQuote}
|
|
350
|
+
setSelectedQuote={setSelectedQuote}
|
|
351
|
+
destinationSymbol={destinationSymbol}
|
|
352
|
+
onContinue={goToOpenWidget}
|
|
353
|
+
/>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
case CardBuyStep.OpenWidget:
|
|
357
|
+
return (
|
|
358
|
+
<OpenWidgetStep
|
|
359
|
+
selectedQuote={selectedQuote!}
|
|
360
|
+
isCreatingSession={meldFlow.isCreatingSession}
|
|
361
|
+
widgetUrl={meldFlow.widgetUrl}
|
|
362
|
+
sessionId={meldFlow.sessionId}
|
|
363
|
+
onCreateSession={handleStartSession}
|
|
364
|
+
onOpenWidget={handleOpenWidget}
|
|
365
|
+
isTransferDetected={isDetected}
|
|
366
|
+
detectedAmount={detectedAmount}
|
|
367
|
+
error={error || meldFlow.sessionError?.message}
|
|
368
|
+
/>
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
default:
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<>
|
|
378
|
+
<Modal.Header>
|
|
379
|
+
<HeaderWrapper>
|
|
380
|
+
<IconButton onClick={goBack} width="40px">
|
|
381
|
+
<Icon
|
|
382
|
+
as={ChevronLeft}
|
|
383
|
+
color="fg.muted"
|
|
384
|
+
width="16px"
|
|
385
|
+
height="16px"
|
|
386
|
+
/>
|
|
387
|
+
</IconButton>
|
|
388
|
+
<Box>
|
|
389
|
+
<HeaderTitle>{STEP_TITLES[currentStep]}</HeaderTitle>
|
|
390
|
+
</Box>
|
|
391
|
+
{handleClose && (
|
|
392
|
+
<IconButton onClick={handleClose} width="40px">
|
|
393
|
+
<Icon
|
|
394
|
+
as={X}
|
|
395
|
+
color="fg.muted"
|
|
396
|
+
width="16px"
|
|
397
|
+
height="16px"
|
|
398
|
+
/>
|
|
399
|
+
</IconButton>
|
|
400
|
+
)}
|
|
401
|
+
</HeaderWrapper>
|
|
402
|
+
</Modal.Header>
|
|
403
|
+
<Modal.Body>
|
|
404
|
+
<AnimatedStep key={currentStep}>
|
|
405
|
+
{renderStepContent()}
|
|
406
|
+
</AnimatedStep>
|
|
407
|
+
</Modal.Body>
|
|
408
|
+
</>
|
|
409
|
+
);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
export default CardBuyFlow;
|