Skip to content

Commit 0eeb9f7

Browse files
feat: redesign payment selection flow for Buy screen
1 parent bafa0e8 commit 0eeb9f7

File tree

9 files changed

+457
-236
lines changed

9 files changed

+457
-236
lines changed

.changeset/sweet-days-admire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Redesigned Pay payment selection flow

apps/playground-web/src/lib/client.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,30 @@ setThirdwebDomains({
1010
pay: process.env.NEXT_PUBLIC_PAY_URL,
1111
});
1212

13+
const isDev =
14+
process.env.NODE_ENV === "development" ||
15+
process.env.NEXT_PUBLIC_RPC_URL?.includes("thirdweb-dev.com");
16+
1317
export const THIRDWEB_CLIENT = createThirdwebClient(
1418
process.env.THIRDWEB_SECRET_KEY
15-
? { secretKey: process.env.THIRDWEB_SECRET_KEY }
19+
? {
20+
secretKey: process.env.THIRDWEB_SECRET_KEY,
21+
config: {
22+
storage: isDev
23+
? {
24+
gatewayUrl: "https://gateway.pinata.cloud/ipfs/",
25+
}
26+
: undefined,
27+
},
28+
}
1629
: {
1730
clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string,
31+
config: {
32+
storage: isDev
33+
? {
34+
gatewayUrl: "https://gateway.pinata.cloud/ipfs/",
35+
}
36+
: undefined,
37+
},
1838
},
1939
);

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

Lines changed: 35 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { IdCardIcon } from "@radix-ui/react-icons";
21
import { useQueryClient } from "@tanstack/react-query";
32
import { useCallback, useMemo, useState } from "react";
4-
import { trackPayEvent } from "../../../../../../analytics/track/pay.js";
53
import type { Chain } from "../../../../../../chains/types.js";
64
import type { ThirdwebClient } from "../../../../../../client/client.js";
75
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../constants/addresses.js";
@@ -14,7 +12,6 @@ import type { Account } from "../../../../../../wallets/interfaces/wallet.js";
1412
import type { WalletId } from "../../../../../../wallets/wallet-types.js";
1513
import {
1614
type Theme,
17-
iconSize,
1815
spacing,
1916
} from "../../../../../core/design-system/index.js";
2017
import type {
@@ -44,7 +41,6 @@ import { Text } from "../../../components/text.js";
4441
import { TokenSymbol } from "../../../components/token/TokenSymbol.js";
4542
import { ConnectButton } from "../../ConnectButton.js";
4643
import { ChainButton, NetworkSelectorContent } from "../../NetworkSelector.js";
47-
import { CoinsIcon } from "../../icons/CoinsIcon.js";
4844
import type { ConnectLocale } from "../../locale/types.js";
4945
import { TokenSelector } from "../TokenSelector.js";
5046
import { WalletSwitcherConnectionScreen } from "../WalletSwitcherConnectionScreen.js";
@@ -72,9 +68,9 @@ import { openOnrampPopup } from "./openOnRamppopup.js";
7268
import { BuyTokenInput } from "./swap/BuyTokenInput.js";
7369
import { FiatFees, SwapFees } from "./swap/Fees.js";
7470
import { PayWithCrypto } from "./swap/PayWithCrypto.js";
71+
import { PaymentSelectionScreen } from "./swap/PaymentSelectionScreen.js";
7572
import { SwapFlow } from "./swap/SwapFlow.js";
7673
import { TransferFlow } from "./swap/TransferFlow.js";
77-
import { WalletSwitcherDrawerContent } from "./swap/WalletSwitcherDrawerContent.js";
7874
import { addPendingTx } from "./swap/pendingSwapTx.js";
7975
import {
8076
type SupportedChainAndTokens,
@@ -222,7 +218,6 @@ function BuyScreenContent(props: BuyScreenContentProps) {
222218
});
223219

224220
const payDisabled =
225-
enabledPaymentMethods.showPaymentSelection === false &&
226221
enabledPaymentMethods.buyWithCryptoEnabled === false &&
227222
enabledPaymentMethods.buyWithFiatEnabled === false;
228223

@@ -515,7 +510,6 @@ function BuyScreenContent(props: BuyScreenContentProps) {
515510
)}
516511

517512
{(screen.id === "select-payment-method" ||
518-
screen.id === "select-wallet" ||
519513
screen.id === "buy-with-crypto" ||
520514
screen.id === "buy-with-fiat") &&
521515
payer && (
@@ -527,44 +521,42 @@ function BuyScreenContent(props: BuyScreenContentProps) {
527521
client={client}
528522
onBack={() => {
529523
if (
530-
enabledPaymentMethods.showPaymentSelection &&
531-
(screen.id === "select-wallet" ||
532-
screen.id === "buy-with-fiat")
524+
enabledPaymentMethods.buyWithCryptoEnabled &&
525+
screen.id === "buy-with-fiat"
533526
) {
534527
setScreen({ id: "select-payment-method" });
535528
} else if (screen.id === "buy-with-crypto") {
536-
setScreen({ id: "select-wallet" });
529+
setScreen({ id: "select-payment-method" });
537530
} else {
538531
setScreen({ id: "main" });
539532
}
540533
}}
541534
>
542535
{screen.id === "select-payment-method" && (
543-
<PaymentMethodSelection
544-
mode={payOptions.mode}
545-
client={client}
546-
walletAddress={payer.account.address}
547-
walletType={payer.wallet.id}
548-
setScreen={(id) => setScreen({ id })}
549-
/>
550-
)}
551-
552-
{screen.id === "select-wallet" && (
553-
<WalletSwitcherDrawerContent
536+
<PaymentSelectionScreen
554537
client={client}
538+
sourceSupportedTokens={sourceSupportedTokens}
555539
hiddenWallets={props.hiddenWallets}
556-
onSelect={(w) => {
557-
const chain = w.getChain();
540+
payWithFiatEnabled={props.payOptions.buyWithFiat !== false}
541+
toChain={toChain}
542+
toToken={toToken}
543+
tokenAmount={tokenAmount}
544+
onSelect={(w, token, chain) => {
558545
const account = w.getAccount();
559-
if (chain && account) {
546+
if (account) {
560547
setPayer({
561548
account,
562549
chain,
563550
wallet: w,
564551
});
552+
setFromToken(token);
553+
setFromChain(chain);
565554
setScreen({ id: "buy-with-crypto" });
566555
}
567556
}}
557+
onSelectFiat={() => {
558+
setScreen({ id: "buy-with-fiat" });
559+
}}
568560
showAllWallets={!!props.connectOptions?.showAllWallets}
569561
wallets={props.connectOptions?.wallets}
570562
onBack={() => {
@@ -574,11 +566,11 @@ function BuyScreenContent(props: BuyScreenContentProps) {
574566
setScreen({
575567
id: "connect-payer-wallet",
576568
backScreen: {
577-
id: "select-wallet",
569+
id: "select-payment-method",
578570
},
579571
});
580572
}}
581-
selectedAddress={payer.account.address}
573+
defaultPayerAddress={payer.account.address}
582574
/>
583575
)}
584576

@@ -736,8 +728,7 @@ function MainScreen(props: {
736728
enabledPaymentMethods,
737729
} = props;
738730

739-
const { showPaymentSelection, buyWithCryptoEnabled, buyWithFiatEnabled } =
740-
enabledPaymentMethods;
731+
const { buyWithCryptoEnabled, buyWithFiatEnabled } = enabledPaymentMethods;
741732
const disableContinue = !tokenAmount;
742733

743734
switch (payOptions.mode) {
@@ -755,15 +746,10 @@ function MainScreen(props: {
755746
setFromChain(toChain);
756747
setFromToken(toToken);
757748
setToToken(toToken);
758-
if (showPaymentSelection) {
759-
props.setScreen({ id: "select-payment-method" });
760-
} else if (buyWithCryptoEnabled) {
761-
props.setScreen({ id: "select-wallet" });
762-
} else if (buyWithFiatEnabled) {
749+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
763750
props.setScreen({ id: "buy-with-fiat" });
764751
} else {
765-
// default to buy with crypto with connected wallet if chain not supported by pay
766-
props.setScreen({ id: "select-wallet" });
752+
props.setScreen({ id: "select-payment-method" });
767753
}
768754
}}
769755
/>
@@ -783,15 +769,10 @@ function MainScreen(props: {
783769
setFromChain(toChain);
784770
setFromToken(toToken);
785771
setToToken(toToken);
786-
if (showPaymentSelection) {
787-
props.setScreen({ id: "select-payment-method" });
788-
} else if (buyWithCryptoEnabled) {
789-
props.setScreen({ id: "buy-with-crypto" });
790-
} else if (buyWithFiatEnabled) {
772+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
791773
props.setScreen({ id: "buy-with-fiat" });
792774
} else {
793-
// default to buy with crypto with connected wallet if chain not supported by pay
794-
props.setScreen({ id: "select-wallet" });
775+
props.setScreen({ id: "select-payment-method" });
795776
}
796777
}}
797778
/>
@@ -846,14 +827,10 @@ function MainScreen(props: {
846827
disabled={disableContinue}
847828
data-disabled={disableContinue}
848829
onClick={() => {
849-
if (showPaymentSelection) {
850-
props.setScreen({ id: "select-payment-method" });
851-
} else if (buyWithCryptoEnabled) {
852-
props.setScreen({ id: "buy-with-crypto" });
853-
} else if (buyWithFiatEnabled) {
830+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
854831
props.setScreen({ id: "buy-with-fiat" });
855832
} else {
856-
console.error("No payment method enabled");
833+
props.setScreen({ id: "select-payment-method" });
857834
}
858835
}}
859836
>
@@ -909,87 +886,6 @@ function TokenSelectedLayout(props: {
909886
);
910887
}
911888

912-
function PaymentMethodSelection(props: {
913-
client: ThirdwebClient;
914-
walletAddress: string;
915-
walletType: string;
916-
setScreen: (screenId: "select-wallet" | "buy-with-fiat") => void;
917-
mode?: "transaction" | "direct_payment" | "fund_wallet";
918-
}) {
919-
return (
920-
<Container animate="fadein">
921-
{/* Credit Card */}
922-
<Container flex="column" gap="sm">
923-
<Button
924-
variant="outline"
925-
bg="tertiaryBg"
926-
onClick={() => {
927-
trackPayEvent({
928-
event: `pay_with_credit_card_${props.mode || "unknown"}_mode`,
929-
client: props.client,
930-
walletAddress: props.walletAddress,
931-
walletType: props.walletType,
932-
});
933-
props.setScreen("buy-with-fiat");
934-
}}
935-
gap="sm"
936-
style={{
937-
justifyContent: "flex-start",
938-
textAlign: "left",
939-
}}
940-
>
941-
<Container color="secondaryText" flex="row" center="both">
942-
<IdCardIcon
943-
style={{
944-
width: iconSize.md,
945-
height: iconSize.md,
946-
}}
947-
/>
948-
</Container>
949-
950-
<Container flex="column" gap="xxs">
951-
<Text size="md" color="primaryText">
952-
Credit Card
953-
</Text>
954-
<Text size="xs">Securely pay with credit card</Text>
955-
</Container>
956-
</Button>
957-
958-
{/* Crypto */}
959-
<Button
960-
variant="outline"
961-
bg="tertiaryBg"
962-
onClick={() => {
963-
trackPayEvent({
964-
event: `pay_with_crypto_${props.mode || "unknown"}_mode`,
965-
client: props.client,
966-
walletAddress: props.walletAddress,
967-
walletType: props.walletType,
968-
});
969-
970-
props.setScreen("select-wallet");
971-
}}
972-
style={{
973-
justifyContent: "flex-start",
974-
}}
975-
gap="sm"
976-
>
977-
<Container color="secondaryText" flex="row" center="both">
978-
<CoinsIcon size={iconSize.md} />
979-
</Container>
980-
981-
<Container flex="column" gap="xxs">
982-
<Text size="md" color="primaryText">
983-
Crypto
984-
</Text>
985-
<Text size="xs">Pay with your connected wallet</Text>
986-
</Container>
987-
</Button>
988-
</Container>
989-
</Container>
990-
);
991-
}
992-
993889
function SwapScreenContent(props: {
994890
setScreen: (screen: SelectedScreen) => void;
995891
tokenAmount: string;
@@ -1020,7 +916,6 @@ function SwapScreenContent(props: {
1020916
toToken,
1021917
fromChain,
1022918
fromToken,
1023-
showFromTokenSelector,
1024919
payOptions,
1025920
disableTokenSelection,
1026921
} = props;
@@ -1197,7 +1092,7 @@ function SwapScreenContent(props: {
11971092
<WalletSelectorButton
11981093
client={props.client}
11991094
onClick={() => {
1200-
setScreen({ id: "select-wallet" });
1095+
setScreen({ id: "select-payment-method" });
12011096
}}
12021097
address={props.payer.account.address}
12031098
walletId={props.payer.wallet.id}
@@ -1209,7 +1104,7 @@ function SwapScreenContent(props: {
12091104

12101105
<PayWithCrypto
12111106
value={sourceTokenAmount || ""}
1212-
onSelectToken={showFromTokenSelector}
1107+
onSelectToken={() => setScreen({ id: "select-payment-method" })}
12131108
chain={fromChain}
12141109
token={fromToken}
12151110
isLoading={quoteQuery.isLoading && !sourceTokenAmount}
@@ -1550,6 +1445,13 @@ function createSupportedTokens(
15501445

15511446
for (const x of data) {
15521447
tokens[x.chain.id] = x.tokens.filter((t) => {
1448+
// for source tokens, data is not provided, so we include all of them
1449+
if (
1450+
t.buyWithCryptoEnabled === undefined &&
1451+
t.buyWithFiatEnabled === undefined
1452+
) {
1453+
return true;
1454+
}
15531455
// it token supports both - include it
15541456
if (t.buyWithCryptoEnabled && t.buyWithFiatEnabled) {
15551457
return true;

0 commit comments

Comments
 (0)