@lifi/widget 4.9.0 → 4.10.1
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/README.md +28 -10
- package/dist/esm/components/TokenList/TokenDetailsSheet.js +5 -8
- package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +30 -2
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +36 -47
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +2 -2
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TokenList/tokenVerificationIcons.js +29 -0
- package/dist/esm/components/TokenList/tokenVerificationIcons.js.map +1 -0
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/usePacedValue.js +1 -1
- package/dist/esm/hooks/usePacedValue.js.map +1 -1
- package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
- package/dist/esm/hooks/useRouteExecutionMessage.js +4 -8
- package/dist/esm/hooks/useRouteExecutionMessage.js.map +1 -1
- package/dist/esm/i18n/bn.json +567 -266
- package/dist/esm/i18n/de.json +552 -251
- package/dist/esm/i18n/en.json +12 -62
- package/dist/esm/i18n/es.json +522 -221
- package/dist/esm/i18n/fr.json +541 -240
- package/dist/esm/i18n/hi.json +592 -291
- package/dist/esm/i18n/id.json +485 -184
- package/dist/esm/i18n/it.json +546 -245
- package/dist/esm/i18n/ja.json +512 -211
- package/dist/esm/i18n/ko.json +553 -252
- package/dist/esm/i18n/pl.json +598 -297
- package/dist/esm/i18n/pt.json +513 -212
- package/dist/esm/i18n/th.json +554 -253
- package/dist/esm/i18n/tr.json +538 -237
- package/dist/esm/i18n/uk.json +514 -213
- package/dist/esm/i18n/vi.json +550 -249
- package/dist/esm/i18n/zh.json +528 -227
- package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +70 -18
- package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
- package/dist/esm/pages/TransactionPage/utils.d.ts +3 -0
- package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
- package/dist/esm/pages/TransactionPage/utils.js +27 -1
- package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
- package/dist/esm/shared.d.ts +2 -2
- package/dist/esm/shared.js +2 -2
- package/dist/esm/stores/routes/utils.d.ts +1 -1
- package/dist/esm/stores/routes/utils.d.ts.map +1 -1
- package/dist/esm/stores/routes/utils.js +13 -1
- package/dist/esm/stores/routes/utils.js.map +1 -1
- package/dist/esm/utils/getErrorMessage.js +4 -0
- package/dist/esm/utils/getErrorMessage.js.map +1 -1
- package/dist/esm/utils/token.js +13 -1
- package/dist/esm/utils/token.js.map +1 -1
- package/package.json +4 -4
- package/src/components/TokenList/TokenDetailsSheet.tsx +5 -9
- package/src/components/TokenList/TokenDetailsSheetContent.tsx +46 -4
- package/src/components/TokenList/TokenListItem.tsx +44 -68
- package/src/components/TokenList/VirtualizedTokenList.tsx +3 -6
- package/src/components/TokenList/tokenVerificationIcons.ts +15 -0
- package/src/components/TokenList/types.ts +3 -11
- package/src/config/version.ts +1 -1
- package/src/hooks/usePacedValue.ts +1 -1
- package/src/hooks/useRouteExecutionMessage.ts +10 -13
- package/src/pages/TransactionPage/TransactionFailedButtons.test.tsx +497 -0
- package/src/pages/TransactionPage/TransactionFailedButtons.tsx +78 -9
- package/src/pages/TransactionPage/utils.ts +81 -0
- package/src/shared.ts +1 -0
- package/src/stores/routes/utils.ts +24 -1
- package/src/utils/getErrorMessage.ts +5 -0
- package/src/utils/token.ts +48 -0
package/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
[](/LICENSE)
|
|
4
4
|
[](https://www.npmjs.com/package/@lifi/widget)
|
|
5
5
|
[](https://www.npmjs.com/package/@lifi/widget)
|
|
6
|
-
[](https://crowdin.com/project/lifi-widget)
|
|
7
6
|
[](https://twitter.com/lifiprotocol)
|
|
8
7
|
|
|
9
8
|
</div>
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
|
|
17
16
|
[**LI.FI Widget**](https://docs.li.fi/widget/overview) features include:
|
|
18
17
|
|
|
19
|
-
- **Provider-based architecture** - Modular blockchain support with separate packages for Ethereum, Bitcoin, Solana, Sui, Tron, and Stellar
|
|
18
|
+
- **Provider-based architecture** - Modular blockchain support with separate packages for Ethereum, Bitcoin, Solana, Sui, Tron, and Stellar, plus Zcash as a destination
|
|
20
19
|
- All ecosystems, chains, bridges, exchanges, and solvers that [LI.FI](https://docs.li.fi/introduction/chains) supports
|
|
21
20
|
- Embeddable variants - compact, wide, and drawer
|
|
22
21
|
- Options to allow or deny certain chains, tokens, bridges, and exchanges
|
|
@@ -179,6 +178,26 @@ npm install @lifi/widget-provider-stellar
|
|
|
179
178
|
yarn add @lifi/widget-provider-stellar
|
|
180
179
|
```
|
|
181
180
|
|
|
181
|
+
**Zcash (destination only):**
|
|
182
|
+
|
|
183
|
+
**pnpm:**
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
pnpm add @lifi/widget-provider-zcash
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**npm:**
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
npm install @lifi/widget-provider-zcash
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**yarn:**
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
yarn add @lifi/widget-provider-zcash
|
|
199
|
+
```
|
|
200
|
+
|
|
182
201
|
**Note:** You only need to install the provider packages for the blockchains you want to support. Each provider package includes its required peer dependencies.
|
|
183
202
|
|
|
184
203
|
### LI.FI Wallet Management
|
|
@@ -225,7 +244,7 @@ npm install @lifi/widget-light
|
|
|
225
244
|
yarn add @lifi/widget-light
|
|
226
245
|
```
|
|
227
246
|
|
|
228
|
-
Ecosystem handlers are available as subpath imports (`@lifi/widget-light/ethereum`, `@lifi/widget-light/solana`, `@lifi/widget-light/bitcoin`, `@lifi/widget-light/sui`, `@lifi/widget-light/tron`) — install the corresponding peer dependencies only for the chains you need. See the [widget-light integration guide](/packages/widget-embedded/README.md) for full setup instructions.
|
|
247
|
+
Ecosystem handlers are available as subpath imports (`@lifi/widget-light/ethereum`, `@lifi/widget-light/solana`, `@lifi/widget-light/bitcoin`, `@lifi/widget-light/sui`, `@lifi/widget-light/tron`) — install the corresponding peer dependencies only for the chains you need. Zcash is not available in widget-light yet. See the [widget-light integration guide](/packages/widget-embedded/README.md) for full setup instructions.
|
|
229
248
|
|
|
230
249
|
## Architecture
|
|
231
250
|
|
|
@@ -239,6 +258,7 @@ The LI.FI Widget uses a **provider-based architecture** that allows you to selec
|
|
|
239
258
|
- **`@lifi/widget-provider-sui`** - Sui support (requires [@mysten/dapp-kit-react](https://sdk.mystenlabs.com/dapp-kit))
|
|
240
259
|
- **`@lifi/widget-provider-tron`** - Tron support (requires [@tronweb3/tronwallet-adapter-react-hooks](https://github.com/tronweb3/tronwallet-adapter))
|
|
241
260
|
- **`@lifi/widget-provider-stellar`** - Stellar support (bundles [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit); no extra install)
|
|
261
|
+
- **`@lifi/widget-provider-zcash`** - Zcash as a destination chain: validates transparent (`t1`/`t3`) receivers and unified (`u1`) receivers that carry an Orchard receiver; no wallet connection and no extra install. Without it the widget offers no ZEC
|
|
242
262
|
- **`@lifi/wallet-management`** - Wallet management UI components
|
|
243
263
|
- **`@lifi/widget-light`** - Lightweight iframe-based integration ([docs](https://docs.li.fi/widget/widget-light-overview))
|
|
244
264
|
|
|
@@ -275,7 +295,7 @@ export const WidgetPage = () => {
|
|
|
275
295
|
};
|
|
276
296
|
```
|
|
277
297
|
|
|
278
|
-
**Multi-chain example** (Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar):
|
|
298
|
+
**Multi-chain example** (Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar, plus Zcash as a destination):
|
|
279
299
|
|
|
280
300
|
```tsx
|
|
281
301
|
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
|
|
@@ -285,12 +305,14 @@ import { SolanaProvider } from '@lifi/widget-provider-solana';
|
|
|
285
305
|
import { StellarProvider } from '@lifi/widget-provider-stellar';
|
|
286
306
|
import { SuiProvider } from '@lifi/widget-provider-sui';
|
|
287
307
|
import { TronProvider } from '@lifi/widget-provider-tron';
|
|
308
|
+
import { ZcashProvider } from '@lifi/widget-provider-zcash';
|
|
288
309
|
|
|
289
310
|
const widgetConfig: WidgetConfig = {
|
|
290
311
|
providers: [
|
|
291
312
|
EthereumProvider(),
|
|
292
313
|
SolanaProvider(),
|
|
293
314
|
BitcoinProvider(),
|
|
315
|
+
ZcashProvider(),
|
|
294
316
|
SuiProvider(),
|
|
295
317
|
TronProvider(),
|
|
296
318
|
StellarProvider(),
|
|
@@ -322,13 +344,9 @@ Visit our [playground](https://playground.li.fi) to see how you can customize yo
|
|
|
322
344
|
|
|
323
345
|
Please visit [LI.FI Widget Documentation](https://docs.li.fi/widget/install-widget).
|
|
324
346
|
|
|
325
|
-
##
|
|
326
|
-
|
|
327
|
-
We appreciate your interest in helping translate our project!
|
|
328
|
-
|
|
329
|
-
If you'd like to contribute translations, please visit our Crowdin project page at [Crowdin LI.FI Widget](https://crowdin.com/project/lifi-widget). Register on Crowdin and you can start translating the project into your preferred language. Your contributions will help make our project accessible to a wider audience around the world.
|
|
347
|
+
## Translations
|
|
330
348
|
|
|
331
|
-
|
|
349
|
+
The widget has 17 languages. The person or the AI agent that changes an English text in `en.json` also translates it into the 16 other languages, in the same PR. To fix a translation, open a PR that changes the locale file. Follow [TRANSLATING.md](./TRANSLATING.md).
|
|
332
350
|
|
|
333
351
|
## Changelog
|
|
334
352
|
|
|
@@ -5,15 +5,13 @@ import { jsx } from "react/jsx-runtime";
|
|
|
5
5
|
//#region src/components/TokenList/TokenDetailsSheet.tsx
|
|
6
6
|
const TokenDetailsSheet = ({ ref }) => {
|
|
7
7
|
const bottomSheetRef = useRef(null);
|
|
8
|
-
const [
|
|
9
|
-
const [chainId, setChainId] = useState(void 0);
|
|
8
|
+
const [listToken, setListToken] = useState(void 0);
|
|
10
9
|
const [withoutContractAddress, setWithoutContractAddress] = useState(false);
|
|
11
10
|
useImperativeHandle(ref, () => ({
|
|
12
11
|
isOpen: () => bottomSheetRef.current?.isOpen(),
|
|
13
|
-
open: (
|
|
14
|
-
|
|
12
|
+
open: (token, noContractAddress) => {
|
|
13
|
+
setListToken(token);
|
|
15
14
|
setWithoutContractAddress(noContractAddress);
|
|
16
|
-
setChainId(chainId);
|
|
17
15
|
bottomSheetRef.current?.open();
|
|
18
16
|
},
|
|
19
17
|
close: () => {
|
|
@@ -25,9 +23,8 @@ const TokenDetailsSheet = ({ ref }) => {
|
|
|
25
23
|
keepMounted: true,
|
|
26
24
|
children: /* @__PURE__ */ jsx(TokenDetailsSheetContent, {
|
|
27
25
|
onClose: () => bottomSheetRef.current?.close(),
|
|
28
|
-
|
|
29
|
-
withoutContractAddress
|
|
30
|
-
chainId
|
|
26
|
+
listToken,
|
|
27
|
+
withoutContractAddress
|
|
31
28
|
})
|
|
32
29
|
});
|
|
33
30
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenDetailsSheet.js","names":[],"sources":["../../../../src/components/TokenList/TokenDetailsSheet.tsx"],"sourcesContent":["import type { JSX, Ref } from 'react'\nimport { useImperativeHandle, useRef, useState } from 'react'\nimport { BottomSheet } from '../BottomSheet/BottomSheet.js'\nimport type { BottomSheetBase } from '../BottomSheet/types.js'\nimport { TokenDetailsSheetContent } from './TokenDetailsSheetContent.js'\nimport type { TokenDetailsSheetBase } from './types.js'\n\nexport const TokenDetailsSheet = ({\n ref,\n}: {\n ref?: Ref<TokenDetailsSheetBase>\n}): JSX.Element => {\n const bottomSheetRef = useRef<BottomSheetBase>(null)\n const [
|
|
1
|
+
{"version":3,"file":"TokenDetailsSheet.js","names":[],"sources":["../../../../src/components/TokenList/TokenDetailsSheet.tsx"],"sourcesContent":["import type { JSX, Ref } from 'react'\nimport { useImperativeHandle, useRef, useState } from 'react'\nimport type { TokenAmount } from '../../types/token.js'\nimport { BottomSheet } from '../BottomSheet/BottomSheet.js'\nimport type { BottomSheetBase } from '../BottomSheet/types.js'\nimport { TokenDetailsSheetContent } from './TokenDetailsSheetContent.js'\nimport type { TokenDetailsSheetBase } from './types.js'\n\nexport const TokenDetailsSheet = ({\n ref,\n}: {\n ref?: Ref<TokenDetailsSheetBase>\n}): JSX.Element => {\n const bottomSheetRef = useRef<BottomSheetBase>(null)\n const [listToken, setListToken] = useState<TokenAmount | undefined>(undefined)\n const [withoutContractAddress, setWithoutContractAddress] = useState(false)\n\n useImperativeHandle(\n ref,\n () => ({\n isOpen: () => bottomSheetRef.current?.isOpen(),\n open: (token: TokenAmount, noContractAddress: boolean) => {\n setListToken(token)\n setWithoutContractAddress(noContractAddress)\n bottomSheetRef.current?.open()\n },\n close: () => {\n bottomSheetRef.current?.close()\n },\n }),\n []\n )\n\n return (\n <BottomSheet ref={bottomSheetRef} keepMounted>\n <TokenDetailsSheetContent\n onClose={() => bottomSheetRef.current?.close()}\n listToken={listToken}\n withoutContractAddress={withoutContractAddress}\n />\n </BottomSheet>\n )\n}\n"],"mappings":";;;;;AAQA,MAAa,qBAAqB,EAChC,UAGiB;CACjB,MAAM,iBAAiB,OAAwB,IAAI;CACnD,MAAM,CAAC,WAAW,gBAAgB,SAAkC,KAAA,CAAS;CAC7E,MAAM,CAAC,wBAAwB,6BAA6B,SAAS,KAAK;CAE1E,oBACE,YACO;EACL,cAAc,eAAe,SAAS,OAAO;EAC7C,OAAO,OAAoB,sBAA+B;GACxD,aAAa,KAAK;GAClB,0BAA0B,iBAAiB;GAC3C,eAAe,SAAS,KAAK;EAC/B;EACA,aAAa;GACX,eAAe,SAAS,MAAM;EAChC;CACF,IACA,CAAC,CACH;CAEA,OACE,oBAAC,aAAD;EAAa,KAAK;EAAgB,aAAA;EAChC,UAAA,oBAAC,0BAAD;GACE,eAAe,eAAe,SAAS,MAAM;GAClC;GACa;EACzB,CAAA;CACU,CAAA;AAEjB"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useAvailableChains } from "../../hooks/useAvailableChains.js";
|
|
2
|
+
import { getTokenStatusTextKey, getTokenVerificationBadge } from "../../utils/token.js";
|
|
2
3
|
import { useToken } from "../../hooks/useToken.js";
|
|
3
4
|
import { CardIconButton } from "../Card/CardIconButton.js";
|
|
4
5
|
import { TokenAvatar } from "../Avatar/TokenAvatar.js";
|
|
5
6
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
6
7
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
7
8
|
import { Label, MetricContainer, TokenDetailsSheetContainer, TokenDetailsSheetHeader } from "./TokenDetailsSheetContent.style.js";
|
|
9
|
+
import { tokenVerificationIcons } from "./tokenVerificationIcons.js";
|
|
8
10
|
import { useMemo } from "react";
|
|
9
11
|
import { Box, IconButton, Link, Skeleton, Typography } from "@mui/material";
|
|
10
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -14,12 +16,17 @@ import OpenInNewRounded from "@mui/icons-material/OpenInNewRounded";
|
|
|
14
16
|
import Close from "@mui/icons-material/Close";
|
|
15
17
|
//#region src/components/TokenList/TokenDetailsSheetContent.tsx
|
|
16
18
|
const noDataLabel = "-";
|
|
17
|
-
const TokenDetailsSheetContent = ({
|
|
19
|
+
const TokenDetailsSheetContent = ({ listToken, withoutContractAddress, onClose }) => {
|
|
18
20
|
const { t } = useTranslation();
|
|
19
21
|
const { getAddressLink } = useExplorer();
|
|
20
22
|
const { getChainById } = useAvailableChains();
|
|
23
|
+
const tokenAddress = listToken?.address;
|
|
24
|
+
const chainId = listToken?.chainId;
|
|
21
25
|
const { token, isLoading } = useToken(chainId, tokenAddress, true);
|
|
22
26
|
const chain = useMemo(() => getChainById(chainId), [chainId, getChainById]);
|
|
27
|
+
const statusBadge = listToken ? getTokenVerificationBadge(listToken) : void 0;
|
|
28
|
+
const statusIcon = statusBadge ? tokenVerificationIcons[statusBadge] : void 0;
|
|
29
|
+
const statusText = listToken ? t(getTokenStatusTextKey(listToken)) : noDataLabel;
|
|
23
30
|
const copyContractAddress = async (e) => {
|
|
24
31
|
e.stopPropagation();
|
|
25
32
|
try {
|
|
@@ -161,7 +168,28 @@ const TokenDetailsSheetContent = ({ tokenAddress, chainId, withoutContractAddres
|
|
|
161
168
|
maximumFractionDigits: 2
|
|
162
169
|
}) : noDataLabel
|
|
163
170
|
})
|
|
164
|
-
})
|
|
171
|
+
}),
|
|
172
|
+
/* @__PURE__ */ jsxs(MetricContainer, { children: [/* @__PURE__ */ jsx(Label, { children: t("tokenMetric.status") }), /* @__PURE__ */ jsxs(Box, {
|
|
173
|
+
sx: {
|
|
174
|
+
display: "flex",
|
|
175
|
+
alignItems: "flex-start",
|
|
176
|
+
gap: 1
|
|
177
|
+
},
|
|
178
|
+
children: [statusIcon ? /* @__PURE__ */ jsx(statusIcon.Icon, { sx: {
|
|
179
|
+
flexShrink: 0,
|
|
180
|
+
fontSize: 20,
|
|
181
|
+
color: statusIcon.color
|
|
182
|
+
} }) : null, /* @__PURE__ */ jsx(Typography, {
|
|
183
|
+
sx: {
|
|
184
|
+
minWidth: 0,
|
|
185
|
+
fontWeight: 500,
|
|
186
|
+
fontSize: "14px",
|
|
187
|
+
lineHeight: "20px",
|
|
188
|
+
color: "text.primary"
|
|
189
|
+
},
|
|
190
|
+
children: statusText
|
|
191
|
+
})]
|
|
192
|
+
})] })
|
|
165
193
|
] });
|
|
166
194
|
};
|
|
167
195
|
const MetricWithSkeleton = ({ label, width, height, isLoading, children }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenDetailsSheetContent.js","names":[],"sources":["../../../../src/components/TokenList/TokenDetailsSheetContent.tsx"],"sourcesContent":["import Close from '@mui/icons-material/Close'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport { Box, IconButton, Link, Skeleton, Typography } from '@mui/material'\nimport type React from 'react'\nimport { type JSX, type PropsWithChildren, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useToken } from '../../hooks/useToken.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { CardIconButton } from '../Card/CardIconButton.js'\nimport {\n Label,\n MetricContainer,\n TokenDetailsSheetContainer,\n TokenDetailsSheetHeader,\n} from './TokenDetailsSheetContent.style.js'\n\ninterface TokenDetailsSheetContentProps {\n tokenAddress: string | undefined\n chainId: number | undefined\n withoutContractAddress: boolean\n onClose: () => void\n}\n\nconst noDataLabel = '-'\n\nexport const TokenDetailsSheetContent = ({\n tokenAddress,\n chainId,\n withoutContractAddress,\n onClose,\n}: TokenDetailsSheetContentProps): JSX.Element => {\n const { t } = useTranslation()\n const { getAddressLink } = useExplorer()\n const { getChainById } = useAvailableChains()\n\n const { token, isLoading } = useToken(chainId, tokenAddress, true)\n const chain = useMemo(() => getChainById(chainId), [chainId, getChainById])\n\n const copyContractAddress = async (e: React.MouseEvent) => {\n e.stopPropagation()\n try {\n // Clipboard API may throw if access is denied (e.g., in insecure contexts or older browsers)\n await navigator.clipboard.writeText(tokenAddress || '')\n } catch {\n // Silently fail to avoid crashing the UI if clipboard write fails\n }\n }\n\n return (\n <TokenDetailsSheetContainer>\n <TokenDetailsSheetHeader>\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 3,\n }}\n >\n <TokenAvatar\n token={token}\n chain={chain}\n tokenAvatarSize={72}\n chainAvatarSize={28}\n isLoading={isLoading}\n />\n <MetricContainer>\n {isLoading ? (\n <>\n <Skeleton variant=\"rounded\" width={80} height={24} />\n <Skeleton variant=\"rounded\" width={80} height={16} />\n </>\n ) : (\n <>\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '24px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.symbol || noDataLabel}\n </Typography>\n <Label>{token?.name || noDataLabel}</Label>\n </>\n )}\n </MetricContainer>\n </Box>\n <IconButton\n onClick={(e) => {\n e.stopPropagation()\n onClose()\n }}\n sx={{ mt: '-8px', mr: '-8px' }}\n >\n <Close />\n </IconButton>\n </TokenDetailsSheetHeader>\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.currentPrice')}\n width={200}\n height={40}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '32px',\n lineHeight: '40px',\n color: 'text.primary',\n }}\n >\n {token?.priceUSD\n ? t('format.currency', {\n value: token.priceUSD,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n {!withoutContractAddress && (\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.contractAddress')}\n width={200}\n height={24}\n >\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 1,\n }}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {shortenAddress(tokenAddress)}\n </Typography>\n {tokenAddress && (\n <CardIconButton size=\"small\" onClick={copyContractAddress}>\n <ContentCopyRounded fontSize=\"inherit\" />\n </CardIconButton>\n )}\n {tokenAddress && (\n <CardIconButton\n size=\"small\"\n LinkComponent={Link}\n href={getAddressLink(tokenAddress, chainId)}\n target=\"_blank\"\n rel=\"nofollow noreferrer\"\n onClick={(e) => e.stopPropagation()}\n >\n <OpenInNewRounded fontSize=\"inherit\" />\n </CardIconButton>\n )}\n </Box>\n </MetricWithSkeleton>\n )}\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.marketCap')}\n width={200}\n height={24}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.marketCapUSD\n ? t('format.currency', {\n value: token.marketCapUSD,\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits: 2,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.volume24h')}\n width={200}\n height={24}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.volumeUSD24H\n ? t('format.currency', {\n value: token.volumeUSD24H,\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits: 2,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n </TokenDetailsSheetContainer>\n )\n}\n\ninterface MetricWithSkeletonProps {\n label: string\n isLoading: boolean\n width: number\n height: number\n}\n\nconst MetricWithSkeleton = ({\n label,\n width,\n height,\n isLoading,\n children,\n}: PropsWithChildren<MetricWithSkeletonProps>) => {\n return (\n <MetricContainer>\n <Label>{label}</Label>\n {isLoading ? (\n <Skeleton variant=\"rounded\" width={width} height={height} />\n ) : (\n children\n )}\n </MetricContainer>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TokenDetailsSheetContent.js","names":[],"sources":["../../../../src/components/TokenList/TokenDetailsSheetContent.tsx"],"sourcesContent":["import Close from '@mui/icons-material/Close'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport { Box, IconButton, Link, Skeleton, Typography } from '@mui/material'\nimport type React from 'react'\nimport { type JSX, type PropsWithChildren, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useToken } from '../../hooks/useToken.js'\nimport type { TokenAmount } from '../../types/token.js'\nimport {\n getTokenStatusTextKey,\n getTokenVerificationBadge,\n} from '../../utils/token.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { CardIconButton } from '../Card/CardIconButton.js'\nimport {\n Label,\n MetricContainer,\n TokenDetailsSheetContainer,\n TokenDetailsSheetHeader,\n} from './TokenDetailsSheetContent.style.js'\nimport { tokenVerificationIcons } from './tokenVerificationIcons.js'\n\ninterface TokenDetailsSheetContentProps {\n listToken: TokenAmount | undefined\n withoutContractAddress: boolean\n onClose: () => void\n}\n\nconst noDataLabel = '-'\n\nexport const TokenDetailsSheetContent = ({\n listToken,\n withoutContractAddress,\n onClose,\n}: TokenDetailsSheetContentProps): JSX.Element => {\n const { t } = useTranslation()\n const { getAddressLink } = useExplorer()\n const { getChainById } = useAvailableChains()\n\n const tokenAddress = listToken?.address\n const chainId = listToken?.chainId\n const { token, isLoading } = useToken(chainId, tokenAddress, true)\n const chain = useMemo(() => getChainById(chainId), [chainId, getChainById])\n\n // The row token, not the fetched one: only the list sets the flags.\n const statusBadge = listToken\n ? getTokenVerificationBadge(listToken)\n : undefined\n const statusIcon = statusBadge\n ? tokenVerificationIcons[statusBadge]\n : undefined\n const statusText = listToken\n ? t(getTokenStatusTextKey(listToken))\n : noDataLabel\n\n const copyContractAddress = async (e: React.MouseEvent) => {\n e.stopPropagation()\n try {\n // Clipboard API may throw if access is denied (e.g., in insecure contexts or older browsers)\n await navigator.clipboard.writeText(tokenAddress || '')\n } catch {\n // Silently fail to avoid crashing the UI if clipboard write fails\n }\n }\n\n return (\n <TokenDetailsSheetContainer>\n <TokenDetailsSheetHeader>\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 3,\n }}\n >\n <TokenAvatar\n token={token}\n chain={chain}\n tokenAvatarSize={72}\n chainAvatarSize={28}\n isLoading={isLoading}\n />\n <MetricContainer>\n {isLoading ? (\n <>\n <Skeleton variant=\"rounded\" width={80} height={24} />\n <Skeleton variant=\"rounded\" width={80} height={16} />\n </>\n ) : (\n <>\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '24px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.symbol || noDataLabel}\n </Typography>\n <Label>{token?.name || noDataLabel}</Label>\n </>\n )}\n </MetricContainer>\n </Box>\n <IconButton\n onClick={(e) => {\n e.stopPropagation()\n onClose()\n }}\n sx={{ mt: '-8px', mr: '-8px' }}\n >\n <Close />\n </IconButton>\n </TokenDetailsSheetHeader>\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.currentPrice')}\n width={200}\n height={40}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '32px',\n lineHeight: '40px',\n color: 'text.primary',\n }}\n >\n {token?.priceUSD\n ? t('format.currency', {\n value: token.priceUSD,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n {!withoutContractAddress && (\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.contractAddress')}\n width={200}\n height={24}\n >\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 1,\n }}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {shortenAddress(tokenAddress)}\n </Typography>\n {tokenAddress && (\n <CardIconButton size=\"small\" onClick={copyContractAddress}>\n <ContentCopyRounded fontSize=\"inherit\" />\n </CardIconButton>\n )}\n {tokenAddress && (\n <CardIconButton\n size=\"small\"\n LinkComponent={Link}\n href={getAddressLink(tokenAddress, chainId)}\n target=\"_blank\"\n rel=\"nofollow noreferrer\"\n onClick={(e) => e.stopPropagation()}\n >\n <OpenInNewRounded fontSize=\"inherit\" />\n </CardIconButton>\n )}\n </Box>\n </MetricWithSkeleton>\n )}\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.marketCap')}\n width={200}\n height={24}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.marketCapUSD\n ? t('format.currency', {\n value: token.marketCapUSD,\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits: 2,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n <MetricWithSkeleton\n isLoading={isLoading}\n label={t('tokenMetric.volume24h')}\n width={200}\n height={24}\n >\n <Typography\n sx={{\n fontWeight: 700,\n fontSize: '18px',\n lineHeight: '24px',\n color: 'text.primary',\n }}\n >\n {token?.volumeUSD24H\n ? t('format.currency', {\n value: token.volumeUSD24H,\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits: 2,\n })\n : noDataLabel}\n </Typography>\n </MetricWithSkeleton>\n <MetricContainer>\n <Label>{t('tokenMetric.status')}</Label>\n <Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>\n {statusIcon ? (\n <statusIcon.Icon\n sx={{\n flexShrink: 0,\n fontSize: 20,\n color: statusIcon.color,\n }}\n />\n ) : null}\n <Typography\n sx={{\n minWidth: 0,\n fontWeight: 500,\n fontSize: '14px',\n lineHeight: '20px',\n color: 'text.primary',\n }}\n >\n {statusText}\n </Typography>\n </Box>\n </MetricContainer>\n </TokenDetailsSheetContainer>\n )\n}\n\ninterface MetricWithSkeletonProps {\n label: string\n isLoading: boolean\n width: number\n height: number\n}\n\nconst MetricWithSkeleton = ({\n label,\n width,\n height,\n isLoading,\n children,\n}: PropsWithChildren<MetricWithSkeletonProps>) => {\n return (\n <MetricContainer>\n <Label>{label}</Label>\n {isLoading ? (\n <Skeleton variant=\"rounded\" width={width} height={height} />\n ) : (\n children\n )}\n </MetricContainer>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { formatTokenAmount, formatTokenPrice } from "../../utils/format.js";
|
|
2
|
-
import { getTokenVerificationProvider } from "../../utils/token.js";
|
|
2
|
+
import { getTokenVerificationBadge, getTokenVerificationProvider } from "../../utils/token.js";
|
|
3
3
|
import { TokenAvatar } from "../Avatar/TokenAvatar.js";
|
|
4
4
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
5
|
+
import { tokenVerificationIcons } from "./tokenVerificationIcons.js";
|
|
5
6
|
import { IconButton as IconButton$1, ListItem as ListItem$1 } from "./TokenList.style.js";
|
|
6
7
|
import { useLongPress } from "../../hooks/useLongPress.js";
|
|
7
8
|
import { ListItemButton as ListItemButton$1 } from "../ListItem/ListItemButton.js";
|
|
@@ -13,8 +14,6 @@ import { Avatar, Box, ListItemAvatar, ListItemText, Skeleton, Slide, Tooltip, Ty
|
|
|
13
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
15
|
import { useTranslation } from "react-i18next";
|
|
15
16
|
import InfoOutlined from "@mui/icons-material/InfoOutlined";
|
|
16
|
-
import ReportRoundedIcon from "@mui/icons-material/ReportRounded";
|
|
17
|
-
import VerifiedIcon from "@mui/icons-material/Verified";
|
|
18
17
|
//#region src/components/TokenList/TokenListItem.tsx
|
|
19
18
|
const TokenListItem = memo(({ onClick, size, start, token, chain, chainName, isBalanceLoading, startAdornment, endAdornment, selected, onShowTokenDetails }) => {
|
|
20
19
|
return /* @__PURE__ */ jsxs(ListItem$1, {
|
|
@@ -48,14 +47,14 @@ const TokenListItemAvatar = memo(({ token }) => {
|
|
|
48
47
|
children: token.symbol?.[0]
|
|
49
48
|
});
|
|
50
49
|
});
|
|
51
|
-
const OpenTokenDetailsButton = ({
|
|
52
|
-
if (!
|
|
50
|
+
const OpenTokenDetailsButton = ({ token, withoutContractAddress, onClick }) => {
|
|
51
|
+
if (!token.address) return null;
|
|
53
52
|
return /* @__PURE__ */ jsx(IconButton$1, {
|
|
54
53
|
size: "small",
|
|
55
54
|
onClick: (e) => {
|
|
56
55
|
e.stopPropagation();
|
|
57
56
|
e.currentTarget.blur();
|
|
58
|
-
onClick(
|
|
57
|
+
onClick(token, withoutContractAddress);
|
|
59
58
|
},
|
|
60
59
|
children: /* @__PURE__ */ jsx(InfoOutlined, {})
|
|
61
60
|
});
|
|
@@ -85,41 +84,33 @@ const TokenListItemButton = memo(({ onClick, token, chain, chainName, isBalanceL
|
|
|
85
84
|
token.priceUSD,
|
|
86
85
|
token.decimals
|
|
87
86
|
]);
|
|
88
|
-
const longPressEvents = useLongPress(() => onShowTokenDetails(token
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
verificationBadge = {
|
|
106
|
-
Icon: VerifiedIcon,
|
|
107
|
-
color: "success.main",
|
|
108
|
-
title: provider ? t("tooltip.tokenVerifiedByProvider", {
|
|
87
|
+
const longPressEvents = useLongPress(() => onShowTokenDetails(token, withoutContractAddress));
|
|
88
|
+
const verificationBadge = getTokenVerificationBadge(token);
|
|
89
|
+
const verificationIcon = verificationBadge ? tokenVerificationIcons[verificationBadge] : void 0;
|
|
90
|
+
let verificationTitle = "";
|
|
91
|
+
switch (verificationBadge) {
|
|
92
|
+
case "native":
|
|
93
|
+
verificationTitle = chainName ? t("tooltip.tokenNativeOnChain", {
|
|
94
|
+
tokenSymbol: token.symbol,
|
|
95
|
+
chainName
|
|
96
|
+
}) : t("tooltip.tokenNative", { tokenSymbol: token.symbol });
|
|
97
|
+
break;
|
|
98
|
+
case "flagged":
|
|
99
|
+
verificationTitle = t("warning.message.tokenFlagged", { tokenSymbol: token.symbol });
|
|
100
|
+
break;
|
|
101
|
+
case "verified": {
|
|
102
|
+
const provider = getTokenVerificationProvider(token);
|
|
103
|
+
verificationTitle = provider ? t("tooltip.tokenVerifiedByProvider", {
|
|
109
104
|
tokenSymbol: token.symbol,
|
|
110
105
|
provider
|
|
111
|
-
}) : t("tooltip.tokenVerified", { tokenSymbol: token.symbol })
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
Icon: ReportRoundedIcon,
|
|
120
|
-
color: "warning.main",
|
|
121
|
-
title: t("warning.message.tokenUnverified")
|
|
122
|
-
};
|
|
106
|
+
}) : t("tooltip.tokenVerified", { tokenSymbol: token.symbol });
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "unverifiedByProvider":
|
|
110
|
+
verificationTitle = t("warning.message.tokenUnverifiedByProvider", { tokenSymbol: token.symbol });
|
|
111
|
+
break;
|
|
112
|
+
case "unverified": verificationTitle = t("warning.message.tokenUnverified");
|
|
113
|
+
}
|
|
123
114
|
return /* @__PURE__ */ jsxs(ListItemButton$1, {
|
|
124
115
|
onClick: handleClick,
|
|
125
116
|
onMouseEnter,
|
|
@@ -146,14 +137,14 @@ const TokenListItemButton = memo(({ onClick, token, chain, chainName, isBalanceL
|
|
|
146
137
|
alignItems: "center",
|
|
147
138
|
gap: .5
|
|
148
139
|
},
|
|
149
|
-
children: [token.symbol,
|
|
150
|
-
title:
|
|
140
|
+
children: [token.symbol, verificationIcon && /* @__PURE__ */ jsx(Tooltip, {
|
|
141
|
+
title: verificationTitle,
|
|
151
142
|
placement: "top",
|
|
152
143
|
arrow: true,
|
|
153
|
-
children: /* @__PURE__ */ jsx(
|
|
144
|
+
children: /* @__PURE__ */ jsx(verificationIcon.Icon, { sx: {
|
|
154
145
|
display: "flex",
|
|
155
146
|
fontSize: 16,
|
|
156
|
-
color:
|
|
147
|
+
color: verificationIcon.color,
|
|
157
148
|
cursor: "help"
|
|
158
149
|
} })
|
|
159
150
|
})]
|
|
@@ -179,9 +170,8 @@ const TokenListItemButton = memo(({ onClick, token, chain, chainName, isBalanceL
|
|
|
179
170
|
mountOnEnter: true,
|
|
180
171
|
children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
181
172
|
/* @__PURE__ */ jsx(OpenTokenDetailsButton, {
|
|
182
|
-
|
|
173
|
+
token,
|
|
183
174
|
withoutContractAddress,
|
|
184
|
-
chainId: token.chainId,
|
|
185
175
|
onClick: onShowTokenDetails
|
|
186
176
|
}),
|
|
187
177
|
/* @__PURE__ */ jsx(PinTokenButton, {
|
|
@@ -233,9 +223,8 @@ const TokenListItemButton = memo(({ onClick, token, chain, chainName, isBalanceL
|
|
|
233
223
|
children: shortenAddress(token.address)
|
|
234
224
|
}), /* @__PURE__ */ jsxs(Box, { children: [
|
|
235
225
|
/* @__PURE__ */ jsx(OpenTokenDetailsButton, {
|
|
236
|
-
|
|
226
|
+
token,
|
|
237
227
|
withoutContractAddress,
|
|
238
|
-
chainId: token.chainId,
|
|
239
228
|
onClick: onShowTokenDetails
|
|
240
229
|
}),
|
|
241
230
|
/* @__PURE__ */ jsx(PinTokenButton, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListItem.js","names":["ListItem","IconButton","InfoOutlinedIcon","ListItemButton"],"sources":["../../../../src/components/TokenList/TokenListItem.tsx"],"sourcesContent":["import type { StaticToken } from '@lifi/sdk'\nimport { ChainType } from '@lifi/sdk'\nimport InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'\nimport ReportRoundedIcon from '@mui/icons-material/ReportRounded'\nimport VerifiedIcon from '@mui/icons-material/Verified'\nimport {\n Avatar,\n Box,\n ListItemAvatar,\n ListItemText,\n listItemSecondaryActionClasses,\n Skeleton,\n Slide,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { JSX, MouseEventHandler } from 'react'\nimport { memo, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useLongPress } from '../../hooks/useLongPress.js'\nimport { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'\nimport { getTokenVerificationProvider } from '../../utils/token.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { ListItemButton } from '../ListItem/ListItemButton.js'\nimport { PinTokenButton } from './PinTokenButton.js'\nimport { RemoveRecentTokenButton } from './RemoveRecentTokenButton.js'\nimport { IconButton, ListItem } from './TokenList.style.js'\nimport type {\n TokenListItemAvatarProps,\n TokenListItemButtonProps,\n TokenListItemProps,\n} from './types.js'\n\nexport const TokenListItem: React.FC<TokenListItemProps> = memo(\n ({\n onClick,\n size,\n start,\n token,\n chain,\n chainName,\n isBalanceLoading,\n startAdornment,\n endAdornment,\n selected,\n onShowTokenDetails,\n }) => {\n return (\n <ListItem\n style={{\n height: `${size}px`,\n transform: `translateY(${start}px)`,\n padding: 0,\n }}\n >\n {startAdornment}\n <TokenListItemButton\n token={token}\n isBalanceLoading={isBalanceLoading}\n onClick={onClick}\n chain={chain}\n chainName={chainName}\n selected={selected}\n onShowTokenDetails={onShowTokenDetails}\n />\n {endAdornment}\n </ListItem>\n )\n }\n)\n\nconst TokenListItemAvatar = memo(({ token }: TokenListItemAvatarProps) => {\n const [isImageLoading, setIsImageLoading] = useState(true)\n\n return (\n <Avatar\n src={token.logoURI}\n alt={token.symbol}\n sx={(theme) =>\n isImageLoading ? { bgcolor: theme.vars.palette.grey[300] } : null\n }\n onLoad={() => setIsImageLoading(false)}\n >\n {token.symbol?.[0]}\n </Avatar>\n )\n})\n\ninterface OpenTokenDetailsButtonProps {\n tokenAddress: string | undefined\n withoutContractAddress: boolean\n chainId: number\n onClick: (\n tokenAddress: string,\n withoutContractAddress: boolean,\n chainId: number\n ) => void\n}\n\nconst OpenTokenDetailsButton = ({\n tokenAddress,\n withoutContractAddress,\n chainId,\n onClick,\n}: OpenTokenDetailsButtonProps) => {\n if (!tokenAddress) {\n return null\n }\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation()\n e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer\n onClick(tokenAddress, withoutContractAddress, chainId)\n }}\n >\n <InfoOutlinedIcon />\n </IconButton>\n )\n}\n\nconst TokenListItemButton: React.FC<TokenListItemButtonProps> = memo(\n ({\n onClick,\n token,\n chain,\n chainName,\n isBalanceLoading,\n selected,\n onShowTokenDetails,\n }) => {\n const { t } = useTranslation()\n const container = useRef(null)\n const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)\n const [showAddress, setShowAddress] = useState(false)\n\n const withoutContractAddress = chain?.chainType === ChainType.UTXO\n\n const onMouseEnter = () => {\n timeoutId.current = setTimeout(() => {\n if (token.address) {\n setShowAddress(true)\n }\n }, 350)\n }\n\n const onMouseLeave = () => {\n clearTimeout(timeoutId.current)\n if (showAddress) {\n setShowAddress(false)\n }\n }\n\n const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {\n e.stopPropagation()\n onClick?.(token.address, token.chainId)\n }\n\n const tokenAmount = useMemo(\n () => formatTokenAmount(token.amount, token.decimals),\n [token.amount, token.decimals]\n )\n\n const tokenPrice = useMemo(\n () => formatTokenPrice(token.amount, token.priceUSD, token.decimals),\n [token.amount, token.priceUSD, token.decimals]\n )\n\n const longPressEvents = useLongPress(() =>\n onShowTokenDetails(token.address, withoutContractAddress, token.chainId)\n )\n\n // The native token is badged ahead of any verdict: the provider screens\n // the native-address convention and calls it a scam on some chains.\n // A token the main list or the integrator vouches for needs no warning;\n // anything else without a clean verdict keeps the amber one.\n let verificationBadge:\n | { Icon: typeof VerifiedIcon; color: string; title: string }\n | undefined\n if (token.native) {\n verificationBadge = {\n Icon: VerifiedIcon,\n color: 'info.main',\n title: chainName\n ? t('tooltip.tokenNativeOnChain', {\n tokenSymbol: token.symbol,\n chainName,\n })\n : t('tooltip.tokenNative', { tokenSymbol: token.symbol }),\n }\n } else if (token.verificationStatus === 'flagged') {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'error.main',\n title: t('warning.message.tokenFlagged', {\n tokenSymbol: token.symbol,\n }),\n }\n } else if (token.verificationStatus === 'verified') {\n const provider = getTokenVerificationProvider(token)\n verificationBadge = {\n Icon: VerifiedIcon,\n color: 'success.main',\n title: provider\n ? t('tooltip.tokenVerifiedByProvider', {\n tokenSymbol: token.symbol,\n provider,\n })\n : t('tooltip.tokenVerified', { tokenSymbol: token.symbol }),\n }\n } else if (\n token.verificationStatus === 'unverified' &&\n !token.listed &&\n !token.verified\n ) {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'warning.main',\n title: t('warning.message.tokenUnverifiedByProvider', {\n tokenSymbol: token.symbol,\n }),\n }\n } else if (!token.verificationStatus && !token.listed && !token.verified) {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'warning.main',\n title: t('warning.message.tokenUnverified'),\n }\n }\n\n return (\n <ListItemButton\n onClick={handleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n {...longPressEvents}\n dense\n selected={selected}\n sx={{\n height: 60,\n marginBottom: '4px',\n // Not a flex-grow item: a band slot's spare height must not stretch the card.\n flex: 'none',\n }}\n >\n <ListItemAvatar>\n {chain ? (\n <TokenAvatar\n token={token as StaticToken}\n chain={chain}\n tokenAvatarSize={40}\n chainAvatarSize={16}\n />\n ) : (\n <TokenListItemAvatar token={token} />\n )}\n </ListItemAvatar>\n <ListItemText\n primary={\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {token.symbol}\n {verificationBadge && (\n <Tooltip title={verificationBadge.title} placement=\"top\" arrow>\n <verificationBadge.Icon\n sx={{\n display: 'flex',\n fontSize: 16,\n color: verificationBadge.color,\n cursor: 'help',\n }}\n />\n </Tooltip>\n )}\n </Box>\n }\n slotProps={{\n secondary: {\n component: 'div',\n },\n }}\n secondary={\n withoutContractAddress ? (\n <Box\n ref={container}\n sx={{\n height: 20,\n display: 'flex',\n }}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n <Box\n sx={{\n position: 'relative',\n }}\n >\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box>\n <OpenTokenDetailsButton\n tokenAddress={token.address}\n withoutContractAddress={withoutContractAddress}\n chainId={token.chainId}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Slide>\n </Box>\n </Box>\n ) : (\n <Box\n ref={container}\n sx={{\n position: 'relative',\n height: 20,\n }}\n >\n <Slide\n direction=\"down\"\n in={!showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n </Slide>\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n }}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n pt: 0.125,\n }}\n >\n {shortenAddress(token.address)}\n </Box>\n <Box>\n <OpenTokenDetailsButton\n tokenAddress={token.address}\n withoutContractAddress={withoutContractAddress}\n chainId={token.chainId}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Box>\n </Slide>\n </Box>\n )\n }\n />\n {isBalanceLoading ? (\n <TokenAmountSkeleton />\n ) : (\n <Box sx={{ textAlign: 'right' }}>\n {token.amount ? (\n <Typography\n noWrap\n sx={{\n fontWeight: 600,\n }}\n title={tokenAmount}\n >\n {t('format.tokenAmount', {\n value: tokenAmount,\n })}\n </Typography>\n ) : null}\n {token.amount && token.priceUSD ? (\n <Typography\n data-price={token.priceUSD}\n sx={{\n fontWeight: 500,\n fontSize: 12,\n color: 'text.secondary',\n }}\n >\n {t('format.currency', {\n value: tokenPrice,\n })}\n </Typography>\n ) : null}\n </Box>\n )}\n </ListItemButton>\n )\n }\n)\n\nexport const TokenListItemSkeleton = (): JSX.Element => {\n return (\n <ListItem\n secondaryAction={<TokenAmountSkeleton />}\n disablePadding\n sx={(theme) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n padding: 0,\n [`& .${listItemSecondaryActionClasses.root}`]: {\n right: theme.spacing(1.5),\n },\n })}\n >\n <ListItemAvatar>\n <Skeleton\n variant=\"circular\"\n width={40}\n height={40}\n sx={{ marginLeft: 1.5, marginRight: 2 }}\n />\n </ListItemAvatar>\n <ListItemText\n primary={<Skeleton variant=\"text\" width={56} height={24} />}\n secondary={<Skeleton variant=\"text\" width={96} height={16} />}\n />\n </ListItem>\n )\n}\n\nconst TokenAmountSkeleton: React.FC = () => {\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-end',\n }}\n >\n <Skeleton variant=\"text\" width={56} height={24} />\n <Skeleton variant=\"text\" width={48} height={16} />\n </Box>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TokenListItem.js","names":["ListItem","IconButton","InfoOutlinedIcon","ListItemButton"],"sources":["../../../../src/components/TokenList/TokenListItem.tsx"],"sourcesContent":["import type { StaticToken } from '@lifi/sdk'\nimport { ChainType } from '@lifi/sdk'\nimport InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'\nimport {\n Avatar,\n Box,\n ListItemAvatar,\n ListItemText,\n listItemSecondaryActionClasses,\n Skeleton,\n Slide,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { JSX, MouseEventHandler } from 'react'\nimport { memo, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useLongPress } from '../../hooks/useLongPress.js'\nimport type { TokenAmount } from '../../types/token.js'\nimport { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'\nimport {\n getTokenVerificationBadge,\n getTokenVerificationProvider,\n} from '../../utils/token.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { ListItemButton } from '../ListItem/ListItemButton.js'\nimport { PinTokenButton } from './PinTokenButton.js'\nimport { RemoveRecentTokenButton } from './RemoveRecentTokenButton.js'\nimport { IconButton, ListItem } from './TokenList.style.js'\nimport { tokenVerificationIcons } from './tokenVerificationIcons.js'\nimport type {\n TokenListItemAvatarProps,\n TokenListItemButtonProps,\n TokenListItemProps,\n} from './types.js'\n\nexport const TokenListItem: React.FC<TokenListItemProps> = memo(\n ({\n onClick,\n size,\n start,\n token,\n chain,\n chainName,\n isBalanceLoading,\n startAdornment,\n endAdornment,\n selected,\n onShowTokenDetails,\n }) => {\n return (\n <ListItem\n style={{\n height: `${size}px`,\n transform: `translateY(${start}px)`,\n padding: 0,\n }}\n >\n {startAdornment}\n <TokenListItemButton\n token={token}\n isBalanceLoading={isBalanceLoading}\n onClick={onClick}\n chain={chain}\n chainName={chainName}\n selected={selected}\n onShowTokenDetails={onShowTokenDetails}\n />\n {endAdornment}\n </ListItem>\n )\n }\n)\n\nconst TokenListItemAvatar = memo(({ token }: TokenListItemAvatarProps) => {\n const [isImageLoading, setIsImageLoading] = useState(true)\n\n return (\n <Avatar\n src={token.logoURI}\n alt={token.symbol}\n sx={(theme) =>\n isImageLoading ? { bgcolor: theme.vars.palette.grey[300] } : null\n }\n onLoad={() => setIsImageLoading(false)}\n >\n {token.symbol?.[0]}\n </Avatar>\n )\n})\n\ninterface OpenTokenDetailsButtonProps {\n token: TokenAmount\n withoutContractAddress: boolean\n onClick: (token: TokenAmount, withoutContractAddress: boolean) => void\n}\n\nconst OpenTokenDetailsButton = ({\n token,\n withoutContractAddress,\n onClick,\n}: OpenTokenDetailsButtonProps) => {\n if (!token.address) {\n return null\n }\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation()\n e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer\n onClick(token, withoutContractAddress)\n }}\n >\n <InfoOutlinedIcon />\n </IconButton>\n )\n}\n\nconst TokenListItemButton: React.FC<TokenListItemButtonProps> = memo(\n ({\n onClick,\n token,\n chain,\n chainName,\n isBalanceLoading,\n selected,\n onShowTokenDetails,\n }) => {\n const { t } = useTranslation()\n const container = useRef(null)\n const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)\n const [showAddress, setShowAddress] = useState(false)\n\n const withoutContractAddress = chain?.chainType === ChainType.UTXO\n\n const onMouseEnter = () => {\n timeoutId.current = setTimeout(() => {\n if (token.address) {\n setShowAddress(true)\n }\n }, 350)\n }\n\n const onMouseLeave = () => {\n clearTimeout(timeoutId.current)\n if (showAddress) {\n setShowAddress(false)\n }\n }\n\n const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {\n e.stopPropagation()\n onClick?.(token.address, token.chainId)\n }\n\n const tokenAmount = useMemo(\n () => formatTokenAmount(token.amount, token.decimals),\n [token.amount, token.decimals]\n )\n\n const tokenPrice = useMemo(\n () => formatTokenPrice(token.amount, token.priceUSD, token.decimals),\n [token.amount, token.priceUSD, token.decimals]\n )\n\n const longPressEvents = useLongPress(() =>\n onShowTokenDetails(token, withoutContractAddress)\n )\n\n const verificationBadge = getTokenVerificationBadge(token)\n const verificationIcon = verificationBadge\n ? tokenVerificationIcons[verificationBadge]\n : undefined\n let verificationTitle = ''\n switch (verificationBadge) {\n case 'native':\n verificationTitle = chainName\n ? t('tooltip.tokenNativeOnChain', {\n tokenSymbol: token.symbol,\n chainName,\n })\n : t('tooltip.tokenNative', { tokenSymbol: token.symbol })\n break\n case 'flagged':\n verificationTitle = t('warning.message.tokenFlagged', {\n tokenSymbol: token.symbol,\n })\n break\n case 'verified': {\n const provider = getTokenVerificationProvider(token)\n verificationTitle = provider\n ? t('tooltip.tokenVerifiedByProvider', {\n tokenSymbol: token.symbol,\n provider,\n })\n : t('tooltip.tokenVerified', { tokenSymbol: token.symbol })\n break\n }\n case 'unverifiedByProvider':\n verificationTitle = t('warning.message.tokenUnverifiedByProvider', {\n tokenSymbol: token.symbol,\n })\n break\n case 'unverified':\n verificationTitle = t('warning.message.tokenUnverified')\n break\n }\n\n return (\n <ListItemButton\n onClick={handleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n {...longPressEvents}\n dense\n selected={selected}\n sx={{\n height: 60,\n marginBottom: '4px',\n // Not a flex-grow item: a band slot's spare height must not stretch the card.\n flex: 'none',\n }}\n >\n <ListItemAvatar>\n {chain ? (\n <TokenAvatar\n token={token as StaticToken}\n chain={chain}\n tokenAvatarSize={40}\n chainAvatarSize={16}\n />\n ) : (\n <TokenListItemAvatar token={token} />\n )}\n </ListItemAvatar>\n <ListItemText\n primary={\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {token.symbol}\n {verificationIcon && (\n <Tooltip title={verificationTitle} placement=\"top\" arrow>\n <verificationIcon.Icon\n sx={{\n display: 'flex',\n fontSize: 16,\n color: verificationIcon.color,\n cursor: 'help',\n }}\n />\n </Tooltip>\n )}\n </Box>\n }\n slotProps={{\n secondary: {\n component: 'div',\n },\n }}\n secondary={\n withoutContractAddress ? (\n <Box\n ref={container}\n sx={{\n height: 20,\n display: 'flex',\n }}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n <Box\n sx={{\n position: 'relative',\n }}\n >\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box>\n <OpenTokenDetailsButton\n token={token}\n withoutContractAddress={withoutContractAddress}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Slide>\n </Box>\n </Box>\n ) : (\n <Box\n ref={container}\n sx={{\n position: 'relative',\n height: 20,\n }}\n >\n <Slide\n direction=\"down\"\n in={!showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n </Slide>\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n }}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n pt: 0.125,\n }}\n >\n {shortenAddress(token.address)}\n </Box>\n <Box>\n <OpenTokenDetailsButton\n token={token}\n withoutContractAddress={withoutContractAddress}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Box>\n </Slide>\n </Box>\n )\n }\n />\n {isBalanceLoading ? (\n <TokenAmountSkeleton />\n ) : (\n <Box sx={{ textAlign: 'right' }}>\n {token.amount ? (\n <Typography\n noWrap\n sx={{\n fontWeight: 600,\n }}\n title={tokenAmount}\n >\n {t('format.tokenAmount', {\n value: tokenAmount,\n })}\n </Typography>\n ) : null}\n {token.amount && token.priceUSD ? (\n <Typography\n data-price={token.priceUSD}\n sx={{\n fontWeight: 500,\n fontSize: 12,\n color: 'text.secondary',\n }}\n >\n {t('format.currency', {\n value: tokenPrice,\n })}\n </Typography>\n ) : null}\n </Box>\n )}\n </ListItemButton>\n )\n }\n)\n\nexport const TokenListItemSkeleton = (): JSX.Element => {\n return (\n <ListItem\n secondaryAction={<TokenAmountSkeleton />}\n disablePadding\n sx={(theme) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n padding: 0,\n [`& .${listItemSecondaryActionClasses.root}`]: {\n right: theme.spacing(1.5),\n },\n })}\n >\n <ListItemAvatar>\n <Skeleton\n variant=\"circular\"\n width={40}\n height={40}\n sx={{ marginLeft: 1.5, marginRight: 2 }}\n />\n </ListItemAvatar>\n <ListItemText\n primary={<Skeleton variant=\"text\" width={56} height={24} />}\n secondary={<Skeleton variant=\"text\" width={96} height={16} />}\n />\n </ListItem>\n )\n}\n\nconst TokenAmountSkeleton: React.FC = () => {\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-end',\n }}\n >\n <Skeleton variant=\"text\" width={56} height={24} />\n <Skeleton variant=\"text\" width={48} height={16} />\n </Box>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedTokenList.d.ts","names":[],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"VirtualizedTokenList.d.ts","names":[],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"mappings":";;;qBAsBa,sBAAsB,GAAG"}
|
|
@@ -22,8 +22,8 @@ const VirtualizedTokenList = ({ tokens, scrollElementRef, chainId, selectedToken
|
|
|
22
22
|
return new Map(chains.map((chain) => [chain.id, chain]));
|
|
23
23
|
}, [chains]);
|
|
24
24
|
const tokenDetailsSheetRef = useRef(null);
|
|
25
|
-
const onShowTokenDetails = useCallback((
|
|
26
|
-
tokenDetailsSheetRef.current?.open(
|
|
25
|
+
const onShowTokenDetails = useCallback((token, noContractAddress) => {
|
|
26
|
+
tokenDetailsSheetRef.current?.open(token, noContractAddress);
|
|
27
27
|
}, []);
|
|
28
28
|
const getItemKey = useCallback((index) => {
|
|
29
29
|
const token = tokens[index];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedTokenList.js","names":["List"],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"sourcesContent":["import { Typography } from '@mui/material'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport type { FC } from 'react'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { getTokenKey } from '../../stores/recentTokens/utils.js'\nimport { isHoistableNative } from '../../utils/tokenList.js'\nimport { createBandResolver } from '../../utils/tokenListBands.js'\nimport { RecentTokensHeader } from './RecentTokensHeader.js'\nimport { RecentTokensToggle } from './RecentTokensToggle.js'\nimport { TokenDetailsSheet } from './TokenDetailsSheet.js'\nimport { List } from './TokenList.style.js'\nimport { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'\nimport type {\n TokenDetailsSheetBase,\n VirtualizedTokenListProps,\n} from './types.js'\n\nconst tokenItemHeight = 64 // 60 + 4px margin-bottom\n\nexport const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({\n tokens,\n scrollElementRef,\n chainId,\n selectedTokenAddress,\n isLoading,\n isBalanceLoading,\n showCategories,\n showPinnedTokens,\n onClick,\n isAllNetworks,\n recentStartIndex = 0,\n recentCount = 0,\n hiddenRecentCount = 0,\n recentExpanded = false,\n onToggleRecent,\n onClearRecent,\n nativeHoisted,\n}) => {\n const { t } = useTranslation()\n\n const { chains } = useAvailableChains()\n\n // Create Set for O(1) chain lookup instead of O(n) find\n const chainsSet = useMemo(() => {\n if (!chains) {\n return undefined\n }\n return new Map(chains.map((chain) => [chain.id, chain]))\n }, [chains])\n\n const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)\n\n const onShowTokenDetails = useCallback(\n (tokenAddress: string, noContractAddress: boolean, chainId: number) => {\n tokenDetailsSheetRef.current?.open(\n tokenAddress,\n noContractAddress,\n chainId\n )\n },\n []\n )\n\n // Rows below the band keep their key as it changes size, so none remount.\n const getItemKey = useCallback(\n (index: number) => {\n const token = tokens[index]\n if (token.recent) {\n return `recent-${token.chainId}-${token.address}`\n }\n const shift = index >= recentStartIndex + recentCount ? recentCount : 0\n return `${token.chainId}-${token.address}-${index - shift}`\n },\n [tokens, recentStartIndex, recentCount]\n )\n\n const resolvedNativeHoisted = useMemo(\n () => nativeHoisted ?? isHoistableNative(tokens[0]),\n [nativeHoisted, tokens]\n )\n\n const showRecentToggle = recentCount > 0 && !!onToggleRecent\n\n // The band copy is on screen; its list row may be far below the fold.\n const bandKeys = useMemo(() => {\n if (!recentCount) {\n return undefined\n }\n const keys = new Set<string>()\n for (let i = recentStartIndex; i < recentStartIndex + recentCount; i++) {\n const token = tokens[i]\n if (token) {\n keys.add(getTokenKey(token.chainId, token.address))\n }\n }\n return keys\n }, [tokens, recentStartIndex, recentCount])\n\n const bands = useMemo(\n () =>\n createBandResolver(tokens, {\n showCategories: !!showCategories,\n showPinnedTokens: !!showPinnedTokens,\n nativeHoisted: resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }),\n [\n tokens,\n showCategories,\n showPinnedTokens,\n resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n ]\n )\n\n const estimateSize = useCallback(\n (index: number) => tokenItemHeight + bands.getRowExtraHeight(index),\n [bands]\n )\n\n const virtualizerConfig = useMemo(\n () => ({\n count: tokens.length,\n overscan: 5,\n getScrollElement: () => scrollElementRef.current,\n estimateSize,\n getItemKey,\n paddingEnd: 8,\n }),\n [tokens.length, estimateSize, getItemKey, scrollElementRef]\n )\n\n const { getVirtualItems, getTotalSize, scrollToIndex, measure } =\n useVirtualizer(virtualizerConfig)\n\n // Address the issue of disappearing tokens on rerender\n useEffect(() => {\n if (scrollElementRef.current) {\n measure()\n }\n }, [measure, scrollElementRef.current])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when chainId changes\n useEffect(() => {\n // Scroll to the top of the list when switching the chains\n if (getVirtualItems().length) {\n scrollToIndex(0, { align: 'start' })\n }\n // Close the token details sheet when switching the chains\n tokenDetailsSheetRef.current?.close()\n }, [scrollToIndex, isAllNetworks, chainId, getVirtualItems])\n\n return (\n <>\n <List\n className=\"long-list\"\n style={{ height: getTotalSize() }}\n disablePadding\n >\n {getVirtualItems().map((item) => {\n const currentToken = tokens[item.index]\n const chain = chainsSet?.get(currentToken.chainId)\n const band = bands.getRowBandLabel(item.index)\n\n const isSelected =\n selectedTokenAddress === currentToken.address &&\n chainId === currentToken.chainId &&\n (!!currentToken.recent ||\n !bandKeys?.has(\n getTokenKey(currentToken.chainId, currentToken.address)\n ))\n\n return (\n <TokenListItem\n key={item.key}\n onClick={onClick}\n size={item.size}\n start={item.start}\n token={currentToken}\n chain={isAllNetworks ? chain : undefined}\n chainName={chain?.name}\n selected={isSelected}\n onShowTokenDetails={onShowTokenDetails}\n isBalanceLoading={isBalanceLoading}\n startAdornment={\n band?.kind === 'recent' ? (\n // Unconditional: the resolver always reserves this height.\n <RecentTokensHeader\n atListStart={band.atListStart}\n onClear={onClearRecent}\n />\n ) : band?.kind === 'text' ? (\n <Typography\n sx={{\n fontSize: 14,\n fontWeight: 600,\n lineHeight: '16px',\n px: 1.5,\n pt: band.atListStart ? 0 : 1,\n pb: 1,\n }}\n >\n {t(band.key)}\n </Typography>\n ) : null\n }\n endAdornment={\n onToggleRecent && bands.isToggleRow(item.index) ? (\n <RecentTokensToggle\n expanded={recentExpanded}\n hiddenCount={hiddenRecentCount}\n onToggle={onToggleRecent}\n />\n ) : null\n }\n />\n )\n })}\n </List>\n <TokenDetailsSheet ref={tokenDetailsSheetRef} />\n {isLoading && (\n <List disablePadding sx={{ cursor: 'default' }}>\n {Array.from({ length: 3 }).map((_, index) => (\n <TokenListItemSkeleton key={index} />\n ))}\n </List>\n )}\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VirtualizedTokenList.js","names":["List"],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"sourcesContent":["import { Typography } from '@mui/material'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport type { FC } from 'react'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { getTokenKey } from '../../stores/recentTokens/utils.js'\nimport type { TokenAmount } from '../../types/token.js'\nimport { isHoistableNative } from '../../utils/tokenList.js'\nimport { createBandResolver } from '../../utils/tokenListBands.js'\nimport { RecentTokensHeader } from './RecentTokensHeader.js'\nimport { RecentTokensToggle } from './RecentTokensToggle.js'\nimport { TokenDetailsSheet } from './TokenDetailsSheet.js'\nimport { List } from './TokenList.style.js'\nimport { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'\nimport type {\n TokenDetailsSheetBase,\n VirtualizedTokenListProps,\n} from './types.js'\n\nconst tokenItemHeight = 64 // 60 + 4px margin-bottom\n\nexport const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({\n tokens,\n scrollElementRef,\n chainId,\n selectedTokenAddress,\n isLoading,\n isBalanceLoading,\n showCategories,\n showPinnedTokens,\n onClick,\n isAllNetworks,\n recentStartIndex = 0,\n recentCount = 0,\n hiddenRecentCount = 0,\n recentExpanded = false,\n onToggleRecent,\n onClearRecent,\n nativeHoisted,\n}) => {\n const { t } = useTranslation()\n\n const { chains } = useAvailableChains()\n\n // Create Set for O(1) chain lookup instead of O(n) find\n const chainsSet = useMemo(() => {\n if (!chains) {\n return undefined\n }\n return new Map(chains.map((chain) => [chain.id, chain]))\n }, [chains])\n\n const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)\n\n const onShowTokenDetails = useCallback(\n (token: TokenAmount, noContractAddress: boolean) => {\n tokenDetailsSheetRef.current?.open(token, noContractAddress)\n },\n []\n )\n\n // Rows below the band keep their key as it changes size, so none remount.\n const getItemKey = useCallback(\n (index: number) => {\n const token = tokens[index]\n if (token.recent) {\n return `recent-${token.chainId}-${token.address}`\n }\n const shift = index >= recentStartIndex + recentCount ? recentCount : 0\n return `${token.chainId}-${token.address}-${index - shift}`\n },\n [tokens, recentStartIndex, recentCount]\n )\n\n const resolvedNativeHoisted = useMemo(\n () => nativeHoisted ?? isHoistableNative(tokens[0]),\n [nativeHoisted, tokens]\n )\n\n const showRecentToggle = recentCount > 0 && !!onToggleRecent\n\n // The band copy is on screen; its list row may be far below the fold.\n const bandKeys = useMemo(() => {\n if (!recentCount) {\n return undefined\n }\n const keys = new Set<string>()\n for (let i = recentStartIndex; i < recentStartIndex + recentCount; i++) {\n const token = tokens[i]\n if (token) {\n keys.add(getTokenKey(token.chainId, token.address))\n }\n }\n return keys\n }, [tokens, recentStartIndex, recentCount])\n\n const bands = useMemo(\n () =>\n createBandResolver(tokens, {\n showCategories: !!showCategories,\n showPinnedTokens: !!showPinnedTokens,\n nativeHoisted: resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }),\n [\n tokens,\n showCategories,\n showPinnedTokens,\n resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n ]\n )\n\n const estimateSize = useCallback(\n (index: number) => tokenItemHeight + bands.getRowExtraHeight(index),\n [bands]\n )\n\n const virtualizerConfig = useMemo(\n () => ({\n count: tokens.length,\n overscan: 5,\n getScrollElement: () => scrollElementRef.current,\n estimateSize,\n getItemKey,\n paddingEnd: 8,\n }),\n [tokens.length, estimateSize, getItemKey, scrollElementRef]\n )\n\n const { getVirtualItems, getTotalSize, scrollToIndex, measure } =\n useVirtualizer(virtualizerConfig)\n\n // Address the issue of disappearing tokens on rerender\n useEffect(() => {\n if (scrollElementRef.current) {\n measure()\n }\n }, [measure, scrollElementRef.current])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when chainId changes\n useEffect(() => {\n // Scroll to the top of the list when switching the chains\n if (getVirtualItems().length) {\n scrollToIndex(0, { align: 'start' })\n }\n // Close the token details sheet when switching the chains\n tokenDetailsSheetRef.current?.close()\n }, [scrollToIndex, isAllNetworks, chainId, getVirtualItems])\n\n return (\n <>\n <List\n className=\"long-list\"\n style={{ height: getTotalSize() }}\n disablePadding\n >\n {getVirtualItems().map((item) => {\n const currentToken = tokens[item.index]\n const chain = chainsSet?.get(currentToken.chainId)\n const band = bands.getRowBandLabel(item.index)\n\n const isSelected =\n selectedTokenAddress === currentToken.address &&\n chainId === currentToken.chainId &&\n (!!currentToken.recent ||\n !bandKeys?.has(\n getTokenKey(currentToken.chainId, currentToken.address)\n ))\n\n return (\n <TokenListItem\n key={item.key}\n onClick={onClick}\n size={item.size}\n start={item.start}\n token={currentToken}\n chain={isAllNetworks ? chain : undefined}\n chainName={chain?.name}\n selected={isSelected}\n onShowTokenDetails={onShowTokenDetails}\n isBalanceLoading={isBalanceLoading}\n startAdornment={\n band?.kind === 'recent' ? (\n // Unconditional: the resolver always reserves this height.\n <RecentTokensHeader\n atListStart={band.atListStart}\n onClear={onClearRecent}\n />\n ) : band?.kind === 'text' ? (\n <Typography\n sx={{\n fontSize: 14,\n fontWeight: 600,\n lineHeight: '16px',\n px: 1.5,\n pt: band.atListStart ? 0 : 1,\n pb: 1,\n }}\n >\n {t(band.key)}\n </Typography>\n ) : null\n }\n endAdornment={\n onToggleRecent && bands.isToggleRow(item.index) ? (\n <RecentTokensToggle\n expanded={recentExpanded}\n hiddenCount={hiddenRecentCount}\n onToggle={onToggleRecent}\n />\n ) : null\n }\n />\n )\n })}\n </List>\n <TokenDetailsSheet ref={tokenDetailsSheetRef} />\n {isLoading && (\n <List disablePadding sx={{ cursor: 'default' }}>\n {Array.from({ length: 3 }).map((_, index) => (\n <TokenListItemSkeleton key={index} />\n ))}\n </List>\n )}\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import ReportRoundedIcon from "@mui/icons-material/ReportRounded";
|
|
2
|
+
import VerifiedIcon from "@mui/icons-material/Verified";
|
|
3
|
+
//#region src/components/TokenList/tokenVerificationIcons.ts
|
|
4
|
+
const tokenVerificationIcons = {
|
|
5
|
+
native: {
|
|
6
|
+
Icon: VerifiedIcon,
|
|
7
|
+
color: "info.main"
|
|
8
|
+
},
|
|
9
|
+
flagged: {
|
|
10
|
+
Icon: ReportRoundedIcon,
|
|
11
|
+
color: "error.main"
|
|
12
|
+
},
|
|
13
|
+
verified: {
|
|
14
|
+
Icon: VerifiedIcon,
|
|
15
|
+
color: "success.main"
|
|
16
|
+
},
|
|
17
|
+
unverifiedByProvider: {
|
|
18
|
+
Icon: ReportRoundedIcon,
|
|
19
|
+
color: "warning.main"
|
|
20
|
+
},
|
|
21
|
+
unverified: {
|
|
22
|
+
Icon: ReportRoundedIcon,
|
|
23
|
+
color: "warning.main"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { tokenVerificationIcons };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=tokenVerificationIcons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenVerificationIcons.js","names":[],"sources":["../../../../src/components/TokenList/tokenVerificationIcons.ts"],"sourcesContent":["import ReportRoundedIcon from '@mui/icons-material/ReportRounded'\nimport VerifiedIcon from '@mui/icons-material/Verified'\nimport type { SvgIcon } from '@mui/material'\nimport type { TokenVerificationBadge } from '../../utils/token.js'\n\nexport const tokenVerificationIcons: Record<\n TokenVerificationBadge,\n { Icon: typeof SvgIcon; color: string }\n> = {\n native: { Icon: VerifiedIcon, color: 'info.main' },\n flagged: { Icon: ReportRoundedIcon, color: 'error.main' },\n verified: { Icon: VerifiedIcon, color: 'success.main' },\n unverifiedByProvider: { Icon: ReportRoundedIcon, color: 'warning.main' },\n unverified: { Icon: ReportRoundedIcon, color: 'warning.main' },\n}\n"],"mappings":";;;AAKA,MAAa,yBAGT;CACF,QAAQ;EAAE,MAAM;EAAc,OAAO;CAAY;CACjD,SAAS;EAAE,MAAM;EAAmB,OAAO;CAAa;CACxD,UAAU;EAAE,MAAM;EAAc,OAAO;CAAe;CACtD,sBAAsB;EAAE,MAAM;EAAmB,OAAO;CAAe;CACvE,YAAY;EAAE,MAAM;EAAmB,OAAO;CAAe;AAC/D"}
|