Skip to content

Commit ad7daa8

Browse files
committed
UI improvements
1 parent ce099a3 commit ad7daa8

File tree

11 files changed

+533
-181
lines changed

11 files changed

+533
-181
lines changed

packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ type SwapWidgetProps = {
3939
onError?: (error: Error) => void;
4040
onCancel?: () => void;
4141
connectOptions?: SwapWidgetConnectOptions;
42+
prefill?: {
43+
buyToken?: {
44+
tokenAddress?: string;
45+
chainId: number;
46+
amount?: string;
47+
};
48+
sellToken?: {
49+
tokenAddress?: string;
50+
chainId: number;
51+
amount?: string;
52+
};
53+
};
4254
};
4355

4456
export function SwapWidget(props: SwapWidgetProps) {
@@ -48,15 +60,7 @@ export function SwapWidget(props: SwapWidgetProps) {
4860
style={props.style}
4961
className={props.className}
5062
>
51-
<SwapWidgetContent
52-
client={props.client}
53-
theme={props.theme}
54-
connectOptions={props.connectOptions}
55-
locale={props.locale}
56-
currency={props.currency}
57-
style={props.style}
58-
showThirdwebBranding={props.showThirdwebBranding}
59-
/>
63+
<SwapWidgetContent {...props} />
6064
</SwapWidgetContainer>
6165
);
6266
}
@@ -149,6 +153,7 @@ function SwapWidgetContent(props: SwapWidgetProps) {
149153
connectOptions={props.connectOptions}
150154
currency={props.currency || "USD"}
151155
activeWalletInfo={activeWalletInfo}
156+
prefill={props.prefill}
152157
onSwap={(quote, selection) => {
153158
setScreen({
154159
quote,

packages/thirdweb/src/react/web/ui/Bridge/swap-widget/select-buy-token.tsx

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { CheckIcon, DiscIcon } from "@radix-ui/react-icons";
22
import { useEffect, useState } from "react";
3-
import type { Token, TokenWithPrices } from "../../../../../bridge/index.js";
3+
import type { Token } from "../../../../../bridge/index.js";
44
import type { BridgeChain } from "../../../../../bridge/types/Chain.js";
55
import type { ThirdwebClient } from "../../../../../client/client.js";
6-
import { getToken } from "../../../../../pay/convert/get-token.js";
76
import {
87
fontSize,
98
iconSize,
@@ -21,14 +20,15 @@ import { Text } from "../../components/text.js";
2120
import { SearchInput } from "./SearchInput.js";
2221
import { SelectChainButton } from "./SelectChainButton.js";
2322
import { SelectBridgeChain } from "./select-chain.js";
23+
import type { TokenSelection } from "./types.js";
2424
import { useBridgeChains } from "./use-bridge-chains.js";
2525
import { useTokens } from "./use-tokens.js";
2626

2727
type SelectBuyTokenProps = {
2828
onBack: () => void;
2929
client: ThirdwebClient;
30-
selectedToken: TokenWithPrices | undefined;
31-
setSelectedToken: (token: TokenWithPrices) => void;
30+
selectedToken: TokenSelection | undefined;
31+
setSelectedToken: (token: TokenSelection) => void;
3232
};
3333

3434
function getDefaultSelectedChain(
@@ -110,8 +110,8 @@ export function SelectBuyTokenUI(
110110
setSelectedChain: (chain: BridgeChain) => void;
111111
search: string;
112112
setSearch: (search: string) => void;
113-
selectedToken: TokenWithPrices | undefined;
114-
setSelectedToken: (token: TokenWithPrices) => void;
113+
selectedToken: TokenSelection | undefined;
114+
setSelectedToken: (token: TokenSelection) => void;
115115
showMore: (() => void) | undefined;
116116
},
117117
) {
@@ -178,7 +178,9 @@ export function SelectBuyTokenUI(
178178
token={token}
179179
client={props.client}
180180
onSelect={props.setSelectedToken}
181-
isSelected={props.selectedToken?.address === token.address}
181+
isSelected={
182+
props.selectedToken?.tokenAddress === token.address
183+
}
182184
/>
183185
))}
184186

@@ -242,10 +244,9 @@ export function SelectBuyTokenUI(
242244
function TokenButton(props: {
243245
token: Token;
244246
client: ThirdwebClient;
245-
onSelect: (tokenWithPrices: TokenWithPrices) => void;
247+
onSelect: (tokenWithPrices: TokenSelection) => void;
246248
isSelected: boolean;
247249
}) {
248-
const [isLoading, setIsLoading] = useState(false);
249250
return (
250251
<Button
251252
variant={props.isSelected ? "secondary" : "ghost-solid"}
@@ -262,14 +263,10 @@ function TokenButton(props: {
262263
}}
263264
gap="sm"
264265
onClick={async () => {
265-
setIsLoading(true);
266-
const tokenWithPrices = await getToken(
267-
props.client,
268-
props.token.address,
269-
props.token.chainId,
270-
);
271-
setIsLoading(false);
272-
props.onSelect(tokenWithPrices);
266+
props.onSelect({
267+
tokenAddress: props.token.address,
268+
chainId: props.token.chainId,
269+
});
273270
}}
274271
>
275272
<Img
@@ -290,24 +287,14 @@ function TokenButton(props: {
290287
</Text>
291288
</div>
292289

293-
{isLoading ? (
294-
<Spinner
295-
color="secondaryText"
296-
size="md"
290+
{props.isSelected && (
291+
<CheckIcon
292+
width={iconSize.md}
293+
height={iconSize.md}
297294
style={{
298295
marginLeft: "auto",
299296
}}
300297
/>
301-
) : (
302-
props.isSelected && (
303-
<CheckIcon
304-
width={iconSize.md}
305-
height={iconSize.md}
306-
style={{
307-
marginLeft: "auto",
308-
}}
309-
/>
310-
)
311298
)}
312299
</Button>
313300
);

packages/thirdweb/src/react/web/ui/Bridge/swap-widget/select-sell-token.tsx

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import {
44
MagnifyingGlassIcon,
55
} from "@radix-ui/react-icons";
66
import { useEffect, useState } from "react";
7-
import type { TokenWithPrices } from "../../../../../bridge/index.js";
87
import type { BridgeChain } from "../../../../../bridge/types/Chain.js";
98
import type { ThirdwebClient } from "../../../../../client/client.js";
10-
import { getToken } from "../../../../../pay/convert/get-token.js";
119
import { toTokens } from "../../../../../utils/units.js";
1210
import {
1311
fontSize,
@@ -27,15 +25,15 @@ import { Spinner } from "../../components/Spinner.js";
2725
import { Text } from "../../components/text.js";
2826
import { SelectChainButton } from "./SelectChainButton.js";
2927
import { SelectBridgeChain } from "./select-chain.js";
30-
import type { ActiveWalletInfo } from "./types.js";
28+
import type { ActiveWalletInfo, TokenSelection } from "./types.js";
3129
import { useBridgeChains } from "./use-bridge-chains.js";
3230
import { type TokenBalance, useTokenBalances } from "./use-tokens.js";
3331

3432
type SelectSellTokenProps = {
3533
onBack: () => void;
3634
client: ThirdwebClient;
37-
selectedToken: TokenWithPrices | undefined;
38-
setSelectedToken: (token: TokenWithPrices) => void;
35+
selectedToken: TokenSelection | undefined;
36+
setSelectedToken: (token: TokenSelection) => void;
3937
};
4038

4139
function getDefaultSelectedChain(
@@ -196,8 +194,8 @@ export function SelectSellTokenConnectedUI(
196194
setSelectedChain: (chain: BridgeChain) => void;
197195
search: string;
198196
setSearch: (search: string) => void;
199-
selectedToken: TokenWithPrices | undefined;
200-
setSelectedToken: (token: TokenWithPrices) => void;
197+
selectedToken: TokenSelection | undefined;
198+
setSelectedToken: (token: TokenSelection) => void;
201199
showAll: (() => void) | undefined;
202200
},
203201
) {
@@ -296,7 +294,7 @@ export function SelectSellTokenConnectedUI(
296294
onSelect={props.setSelectedToken}
297295
isSelected={
298296
props.selectedToken
299-
? props.selectedToken.address.toLowerCase() ===
297+
? props.selectedToken.tokenAddress?.toLowerCase() ===
300298
token.token_address.toLowerCase()
301299
: false
302300
}
@@ -364,10 +362,9 @@ export function SelectSellTokenConnectedUI(
364362
function TokenButton(props: {
365363
token: TokenBalance;
366364
client: ThirdwebClient;
367-
onSelect: (tokenWithPrices: TokenWithPrices) => void;
365+
onSelect: (tokenWithPrices: TokenSelection) => void;
368366
isSelected: boolean;
369367
}) {
370-
const [isLoading, setIsLoading] = useState(false);
371368
const tokenBalanceInUnits = toTokens(
372369
BigInt(props.token.balance),
373370
props.token.decimals,
@@ -391,14 +388,10 @@ function TokenButton(props: {
391388
}}
392389
gap="sm"
393390
onClick={async () => {
394-
setIsLoading(true);
395-
const tokenWithPrices = await getToken(
396-
props.client,
397-
props.token.token_address,
398-
props.token.chain_id,
399-
);
400-
setIsLoading(false);
401-
props.onSelect(tokenWithPrices);
391+
props.onSelect({
392+
tokenAddress: props.token.token_address,
393+
chainId: props.token.chain_id,
394+
});
402395
}}
403396
>
404397
<Img
@@ -431,13 +424,9 @@ function TokenButton(props: {
431424
width: "100%",
432425
}}
433426
>
434-
<Container flex="row" gap="xs" style={{ alignItems: "center" }}>
435-
<Text size="md" color="primaryText" weight={500}>
436-
{props.token.symbol}
437-
</Text>
438-
439-
{isLoading && <Spinner color="secondaryText" size="sm" />}
440-
</Container>
427+
<Text size="md" color="primaryText" weight={500}>
428+
{props.token.symbol}
429+
</Text>
441430
<div>
442431
{formatTokenAmount(
443432
BigInt(props.token.balance),
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { z } from "zod";
2+
3+
const tokenSelectionSchema = z.object({
4+
tokenAddress: z.string().min(1),
5+
chainId: z.number().int().positive(),
6+
});
7+
8+
const lastUsedTokensSchema = z.object({
9+
buyToken: tokenSelectionSchema.optional(),
10+
sellToken: tokenSelectionSchema.optional(),
11+
});
12+
13+
type LastUsedTokens = z.infer<typeof lastUsedTokensSchema>;
14+
15+
const STORAGE_KEY = "tw.swap.lastUsedTokens";
16+
17+
function isBrowser() {
18+
return (
19+
typeof window !== "undefined" && typeof window.localStorage !== "undefined"
20+
);
21+
}
22+
23+
export function getLastUsedTokens(): LastUsedTokens | undefined {
24+
if (!isBrowser()) {
25+
return undefined;
26+
}
27+
try {
28+
const raw = window.localStorage.getItem(STORAGE_KEY);
29+
if (!raw) {
30+
return undefined;
31+
}
32+
const parsed = JSON.parse(raw);
33+
const result = lastUsedTokensSchema.safeParse(parsed);
34+
if (!result.success) {
35+
return undefined;
36+
}
37+
return result.data;
38+
} catch {
39+
return undefined;
40+
}
41+
}
42+
43+
export function setLastUsedTokens(update: LastUsedTokens): void {
44+
if (!isBrowser()) {
45+
return;
46+
}
47+
try {
48+
const result = lastUsedTokensSchema.safeParse(update);
49+
if (!result.success) {
50+
return;
51+
}
52+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(result.data));
53+
} catch {
54+
// ignore write errors
55+
}
56+
}

0 commit comments

Comments
 (0)