@lifi/widget 4.9.0 → 4.10.0
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 +26 -3
- 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/i18n/bn.json +5 -1
- package/dist/esm/i18n/de.json +5 -1
- package/dist/esm/i18n/en.json +5 -1
- package/dist/esm/i18n/es.json +5 -1
- package/dist/esm/i18n/fr.json +5 -1
- package/dist/esm/i18n/hi.json +5 -1
- package/dist/esm/i18n/id.json +5 -1
- package/dist/esm/i18n/it.json +5 -1
- package/dist/esm/i18n/ja.json +5 -1
- package/dist/esm/i18n/ko.json +5 -1
- package/dist/esm/i18n/pl.json +5 -1
- package/dist/esm/i18n/pt.json +5 -1
- package/dist/esm/i18n/th.json +5 -1
- package/dist/esm/i18n/tr.json +5 -1
- package/dist/esm/i18n/uk.json +5 -1
- package/dist/esm/i18n/vi.json +5 -1
- package/dist/esm/i18n/zh.json +5 -1
- package/dist/esm/utils/token.js +13 -1
- package/dist/esm/utils/token.js.map +1 -1
- package/package.json +1 -1
- 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/utils/token.ts +48 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
[**LI.FI Widget**](https://docs.li.fi/widget/overview) features include:
|
|
18
18
|
|
|
19
|
-
- **Provider-based architecture** - Modular blockchain support with separate packages for Ethereum, Bitcoin, Solana, Sui, Tron, and Stellar
|
|
19
|
+
- **Provider-based architecture** - Modular blockchain support with separate packages for Ethereum, Bitcoin, Solana, Sui, Tron, and Stellar, plus Zcash as a destination
|
|
20
20
|
- All ecosystems, chains, bridges, exchanges, and solvers that [LI.FI](https://docs.li.fi/introduction/chains) supports
|
|
21
21
|
- Embeddable variants - compact, wide, and drawer
|
|
22
22
|
- Options to allow or deny certain chains, tokens, bridges, and exchanges
|
|
@@ -179,6 +179,26 @@ npm install @lifi/widget-provider-stellar
|
|
|
179
179
|
yarn add @lifi/widget-provider-stellar
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
+
**Zcash (destination only):**
|
|
183
|
+
|
|
184
|
+
**pnpm:**
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
pnpm add @lifi/widget-provider-zcash
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**npm:**
|
|
191
|
+
|
|
192
|
+
```sh
|
|
193
|
+
npm install @lifi/widget-provider-zcash
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**yarn:**
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
yarn add @lifi/widget-provider-zcash
|
|
200
|
+
```
|
|
201
|
+
|
|
182
202
|
**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
203
|
|
|
184
204
|
### LI.FI Wallet Management
|
|
@@ -225,7 +245,7 @@ npm install @lifi/widget-light
|
|
|
225
245
|
yarn add @lifi/widget-light
|
|
226
246
|
```
|
|
227
247
|
|
|
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.
|
|
248
|
+
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
249
|
|
|
230
250
|
## Architecture
|
|
231
251
|
|
|
@@ -239,6 +259,7 @@ The LI.FI Widget uses a **provider-based architecture** that allows you to selec
|
|
|
239
259
|
- **`@lifi/widget-provider-sui`** - Sui support (requires [@mysten/dapp-kit-react](https://sdk.mystenlabs.com/dapp-kit))
|
|
240
260
|
- **`@lifi/widget-provider-tron`** - Tron support (requires [@tronweb3/tronwallet-adapter-react-hooks](https://github.com/tronweb3/tronwallet-adapter))
|
|
241
261
|
- **`@lifi/widget-provider-stellar`** - Stellar support (bundles [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit); no extra install)
|
|
262
|
+
- **`@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
263
|
- **`@lifi/wallet-management`** - Wallet management UI components
|
|
243
264
|
- **`@lifi/widget-light`** - Lightweight iframe-based integration ([docs](https://docs.li.fi/widget/widget-light-overview))
|
|
244
265
|
|
|
@@ -275,7 +296,7 @@ export const WidgetPage = () => {
|
|
|
275
296
|
};
|
|
276
297
|
```
|
|
277
298
|
|
|
278
|
-
**Multi-chain example** (Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar):
|
|
299
|
+
**Multi-chain example** (Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar, plus Zcash as a destination):
|
|
279
300
|
|
|
280
301
|
```tsx
|
|
281
302
|
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
|
|
@@ -285,12 +306,14 @@ import { SolanaProvider } from '@lifi/widget-provider-solana';
|
|
|
285
306
|
import { StellarProvider } from '@lifi/widget-provider-stellar';
|
|
286
307
|
import { SuiProvider } from '@lifi/widget-provider-sui';
|
|
287
308
|
import { TronProvider } from '@lifi/widget-provider-tron';
|
|
309
|
+
import { ZcashProvider } from '@lifi/widget-provider-zcash';
|
|
288
310
|
|
|
289
311
|
const widgetConfig: WidgetConfig = {
|
|
290
312
|
providers: [
|
|
291
313
|
EthereumProvider(),
|
|
292
314
|
SolanaProvider(),
|
|
293
315
|
BitcoinProvider(),
|
|
316
|
+
ZcashProvider(),
|
|
294
317
|
SuiProvider(),
|
|
295
318
|
TronProvider(),
|
|
296
319
|
StellarProvider(),
|
|
@@ -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":";;;;;;;;;;;;;;;;;AAgCA,MAAM,cAAc;AAEpB,MAAa,4BAA4B,EACvC,WACA,wBACA,cACgD;CAChD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,mBAAmB,YAAY;CACvC,MAAM,EAAE,iBAAiB,mBAAmB;CAE5C,MAAM,eAAe,WAAW;CAChC,MAAM,UAAU,WAAW;CAC3B,MAAM,EAAE,OAAO,cAAc,SAAS,SAAS,cAAc,IAAI;CACjE,MAAM,QAAQ,cAAc,aAAa,OAAO,GAAG,CAAC,SAAS,YAAY,CAAC;CAG1E,MAAM,cAAc,YAChB,0BAA0B,SAAS,IACnC,KAAA;CACJ,MAAM,aAAa,cACf,uBAAuB,eACvB,KAAA;CACJ,MAAM,aAAa,YACf,EAAE,sBAAsB,SAAS,CAAC,IAClC;CAEJ,MAAM,sBAAsB,OAAO,MAAwB;EACzD,EAAE,gBAAgB;EAClB,IAAI;GAEF,MAAM,UAAU,UAAU,UAAU,gBAAgB,EAAE;EACxD,QAAQ,CAER;CACF;CAEA,OACE,qBAAC,4BAAD,EAAA,UAAA;EACE,qBAAC,yBAAD,EAAA,UAAA,CACE,qBAAC,KAAD;GACE,IAAI;IACF,SAAS;IACT,eAAe;IACf,YAAY;IACZ,KAAK;GACP;GANF,UAAA,CAQE,oBAAC,aAAD;IACS;IACA;IACP,iBAAiB;IACjB,iBAAiB;IACN;GACZ,CAAA,GACD,oBAAC,iBAAD,EAAA,UACG,YACC,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;IAAU,SAAQ;IAAU,OAAO;IAAI,QAAQ;GAAK,CAAA,GACpD,oBAAC,UAAD;IAAU,SAAQ;IAAU,OAAO;IAAI,QAAQ;GAAK,CAAA,CACpD,EAAA,CAAA,IAEF,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,YAAD;IACE,IAAI;KACF,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,OAAO;IACT;IAEC,UAAA,OAAO,UAAU;GACR,CAAA,GACZ,oBAAC,OAAD,EAAA,UAAQ,OAAO,QAAQ,YAAmB,CAAA,CAC1C,EAAA,CAAA,EAEW,CAAA,CACd;EACL,CAAA,GAAA,oBAAC,YAAD;GACE,UAAU,MAAM;IACd,EAAE,gBAAgB;IAClB,QAAQ;GACV;GACA,IAAI;IAAE,IAAI;IAAQ,IAAI;GAAO;GAE7B,UAAA,oBAAC,OAAD,CAAQ,CAAA;EACE,CAAA,CACW,EAAA,CAAA;EACzB,oBAAC,oBAAD;GACa;GACX,OAAO,EAAE,0BAA0B;GACnC,OAAO;GACP,QAAQ;GAER,UAAA,oBAAC,YAAD;IACE,IAAI;KACF,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,OAAO;IACT;IAEC,UAAA,OAAO,WACJ,EAAE,mBAAmB,EACnB,OAAO,MAAM,SACf,CAAC,IACD;GACM,CAAA;EACM,CAAA;EACnB,CAAC,0BACA,oBAAC,oBAAD;GACa;GACX,OAAO,EAAE,6BAA6B;GACtC,OAAO;GACP,QAAQ;GAER,UAAA,qBAAC,KAAD;IACE,IAAI;KACF,SAAS;KACT,eAAe;KACf,YAAY;KACZ,KAAK;IACP;IANF,UAAA;KAQE,oBAAC,YAAD;MACE,IAAI;OACF,YAAY;OACZ,UAAU;OACV,YAAY;OACZ,OAAO;MACT;MAEC,UAAA,eAAe,YAAY;KAClB,CAAA;KACX,gBACC,oBAAC,gBAAD;MAAgB,MAAK;MAAQ,SAAS;MACpC,UAAA,oBAAC,oBAAD,EAAoB,UAAS,UAAW,CAAA;KAC1B,CAAA;KAEjB,gBACC,oBAAC,gBAAD;MACE,MAAK;MACL,eAAe;MACf,MAAM,eAAe,cAAc,OAAO;MAC1C,QAAO;MACP,KAAI;MACJ,UAAU,MAAM,EAAE,gBAAgB;MAElC,UAAA,oBAAC,kBAAD,EAAkB,UAAS,UAAW,CAAA;KACxB,CAAA;IAEf;;EACa,CAAA;EAEtB,oBAAC,oBAAD;GACa;GACX,OAAO,EAAE,uBAAuB;GAChC,OAAO;GACP,QAAQ;GAER,UAAA,oBAAC,YAAD;IACE,IAAI;KACF,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,OAAO;IACT;IAEC,UAAA,OAAO,eACJ,EAAE,mBAAmB;KACnB,OAAO,MAAM;KACb,UAAU;KACV,gBAAgB;KAChB,uBAAuB;IACzB,CAAC,IACD;GACM,CAAA;EACM,CAAA;EACpB,oBAAC,oBAAD;GACa;GACX,OAAO,EAAE,uBAAuB;GAChC,OAAO;GACP,QAAQ;GAER,UAAA,oBAAC,YAAD;IACE,IAAI;KACF,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,OAAO;IACT;IAEC,UAAA,OAAO,eACJ,EAAE,mBAAmB;KACnB,OAAO,MAAM;KACb,UAAU;KACV,gBAAgB;KAChB,uBAAuB;IACzB,CAAC,IACD;GACM,CAAA;EACM,CAAA;EACpB,qBAAC,iBAAD,EAAA,UAAA,CACE,oBAAC,OAAD,EAAA,UAAQ,EAAE,oBAAoB,EAAS,CAAA,GACvC,qBAAC,KAAD;GAAK,IAAI;IAAE,SAAS;IAAQ,YAAY;IAAc,KAAK;GAAE;GAA7D,UAAA,CACG,aACC,oBAAC,WAAW,MAAZ,EACE,IAAI;IACF,YAAY;IACZ,UAAU;IACV,OAAO,WAAW;GACpB,EACD,CAAA,IACC,MACJ,oBAAC,YAAD;IACE,IAAI;KACF,UAAU;KACV,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,OAAO;IACT;IAEC,UAAA;GACS,CAAA,CACT;EACU,CAAA,CAAA,EAAA,CAAA;CACS,EAAA,CAAA;AAEhC;AASA,MAAM,sBAAsB,EAC1B,OACA,OACA,QACA,WACA,eACgD;CAChD,OACE,qBAAC,iBAAD,EAAA,UAAA,CACE,oBAAC,OAAD,EAAA,UAAQ,MAAa,CAAA,GACpB,YACC,oBAAC,UAAD;EAAU,SAAQ;EAAiB;EAAe;CAAS,CAAA,IAE3D,QAEa,EAAA,CAAA;AAErB"}
|
|
@@ -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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.10.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
|