Skip to content

Commit b13649d

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

File tree

8 files changed

+445
-234
lines changed

8 files changed

+445
-234
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: 33 additions & 132 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,41 @@ 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+
onSelect={(w, token, chain) => {
558544
const account = w.getAccount();
559-
if (chain && account) {
545+
if (account) {
560546
setPayer({
561547
account,
562548
chain,
563549
wallet: w,
564550
});
551+
setFromToken(token);
552+
setFromChain(chain);
565553
setScreen({ id: "buy-with-crypto" });
566554
}
567555
}}
556+
onSelectFiat={() => {
557+
setScreen({ id: "buy-with-fiat" });
558+
}}
568559
showAllWallets={!!props.connectOptions?.showAllWallets}
569560
wallets={props.connectOptions?.wallets}
570561
onBack={() => {
@@ -574,7 +565,7 @@ function BuyScreenContent(props: BuyScreenContentProps) {
574565
setScreen({
575566
id: "connect-payer-wallet",
576567
backScreen: {
577-
id: "select-wallet",
568+
id: "select-payment-method",
578569
},
579570
});
580571
}}
@@ -736,8 +727,7 @@ function MainScreen(props: {
736727
enabledPaymentMethods,
737728
} = props;
738729

739-
const { showPaymentSelection, buyWithCryptoEnabled, buyWithFiatEnabled } =
740-
enabledPaymentMethods;
730+
const { buyWithCryptoEnabled, buyWithFiatEnabled } = enabledPaymentMethods;
741731
const disableContinue = !tokenAmount;
742732

743733
switch (payOptions.mode) {
@@ -755,15 +745,10 @@ function MainScreen(props: {
755745
setFromChain(toChain);
756746
setFromToken(toToken);
757747
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) {
748+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
763749
props.setScreen({ id: "buy-with-fiat" });
764750
} else {
765-
// default to buy with crypto with connected wallet if chain not supported by pay
766-
props.setScreen({ id: "select-wallet" });
751+
props.setScreen({ id: "select-payment-method" });
767752
}
768753
}}
769754
/>
@@ -783,15 +768,10 @@ function MainScreen(props: {
783768
setFromChain(toChain);
784769
setFromToken(toToken);
785770
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) {
771+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
791772
props.setScreen({ id: "buy-with-fiat" });
792773
} else {
793-
// default to buy with crypto with connected wallet if chain not supported by pay
794-
props.setScreen({ id: "select-wallet" });
774+
props.setScreen({ id: "select-payment-method" });
795775
}
796776
}}
797777
/>
@@ -846,14 +826,10 @@ function MainScreen(props: {
846826
disabled={disableContinue}
847827
data-disabled={disableContinue}
848828
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) {
829+
if (buyWithFiatEnabled && !buyWithCryptoEnabled) {
854830
props.setScreen({ id: "buy-with-fiat" });
855831
} else {
856-
console.error("No payment method enabled");
832+
props.setScreen({ id: "select-payment-method" });
857833
}
858834
}}
859835
>
@@ -909,87 +885,6 @@ function TokenSelectedLayout(props: {
909885
);
910886
}
911887

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-
993888
function SwapScreenContent(props: {
994889
setScreen: (screen: SelectedScreen) => void;
995890
tokenAmount: string;
@@ -1020,7 +915,6 @@ function SwapScreenContent(props: {
1020915
toToken,
1021916
fromChain,
1022917
fromToken,
1023-
showFromTokenSelector,
1024918
payOptions,
1025919
disableTokenSelection,
1026920
} = props;
@@ -1197,7 +1091,7 @@ function SwapScreenContent(props: {
11971091
<WalletSelectorButton
11981092
client={props.client}
11991093
onClick={() => {
1200-
setScreen({ id: "select-wallet" });
1094+
setScreen({ id: "select-payment-method" });
12011095
}}
12021096
address={props.payer.account.address}
12031097
walletId={props.payer.wallet.id}
@@ -1209,7 +1103,7 @@ function SwapScreenContent(props: {
12091103

12101104
<PayWithCrypto
12111105
value={sourceTokenAmount || ""}
1212-
onSelectToken={showFromTokenSelector}
1106+
onSelectToken={() => setScreen({ id: "select-payment-method" })}
12131107
chain={fromChain}
12141108
token={fromToken}
12151109
isLoading={quoteQuery.isLoading && !sourceTokenAmount}
@@ -1550,6 +1444,13 @@ function createSupportedTokens(
15501444

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

0 commit comments

Comments
 (0)