import type { StaticToken } from '@lifi/sdk' import { ChainType } from '@lifi/sdk' import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined' import WarningRounded from '@mui/icons-material/WarningRounded' import { Avatar, Box, ListItemAvatar, ListItemText, listItemSecondaryActionClasses, Skeleton, Slide, Tooltip, Typography, } from '@mui/material' import type { MouseEventHandler } from 'react' import { memo, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useLongPress } from '../../hooks/useLongPress.js' import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js' import { shortenAddress } from '../../utils/wallet.js' import { TokenAvatar } from '../Avatar/TokenAvatar.js' import { ListItemButton } from '../ListItem/ListItemButton.js' import { IconButton, ListItem } from './TokenList.style.js' import type { TokenListItemAvatarProps, TokenListItemButtonProps, TokenListItemProps, } from './types.js' export const TokenListItem: React.FC = memo( ({ onClick, size, start, token, chain, isBalanceLoading, startAdornment, endAdornment, selected, onShowTokenDetails, }) => { return ( {startAdornment} {endAdornment} ) } ) const TokenListItemAvatar = memo(({ token }: TokenListItemAvatarProps) => { const [isImageLoading, setIsImageLoading] = useState(true) return ( isImageLoading ? { bgcolor: theme.vars.palette.grey[300] } : null } onLoad={() => setIsImageLoading(false)} > {token.symbol?.[0]} ) }) interface OpenTokenDetailsButtonProps { tokenAddress: string | undefined withoutContractAddress: boolean chainId: number onClick: ( tokenAddress: string, withoutContractAddress: boolean, chainId: number ) => void } const OpenTokenDetailsButton = ({ tokenAddress, withoutContractAddress, chainId, onClick, }: OpenTokenDetailsButtonProps) => { if (!tokenAddress) { return null } return ( { e.stopPropagation() e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer onClick(tokenAddress, withoutContractAddress, chainId) }} > ) } const TokenListItemButton: React.FC = memo( ({ onClick, token, chain, isBalanceLoading, selected, onShowTokenDetails, }) => { const { t } = useTranslation() const container = useRef(null) const timeoutId = useRef>(undefined) const [showAddress, setShowAddress] = useState(false) const withoutContractAddress = chain?.chainType === ChainType.UTXO const onMouseEnter = () => { timeoutId.current = setTimeout(() => { if (token.address) { setShowAddress(true) } }, 350) } const onMouseLeave = () => { clearTimeout(timeoutId.current) if (showAddress) { setShowAddress(false) } } const handleClick: MouseEventHandler = (e) => { e.stopPropagation() onClick?.(token.address, token.chainId) } const tokenAmount = useMemo( () => formatTokenAmount(token.amount, token.decimals), [token.amount, token.decimals] ) const tokenPrice = useMemo( () => formatTokenPrice(token.amount, token.priceUSD, token.decimals), [token.amount, token.priceUSD, token.decimals] ) const longPressEvents = useLongPress(() => onShowTokenDetails(token.address, withoutContractAddress, token.chainId) ) return ( {chain ? ( ) : ( )} {token.symbol} {!token.verified && ( )} } slotProps={{ secondary: { component: 'div', }, }} secondary={ withoutContractAddress ? ( {token.name} ) : ( {token.name} {shortenAddress(token.address)} ) } /> {isBalanceLoading ? ( ) : ( {token.amount ? ( {t('format.tokenAmount', { value: tokenAmount, })} ) : null} {token.amount && token.priceUSD ? ( {t('format.currency', { value: tokenPrice, })} ) : null} )} ) } ) export const TokenListItemSkeleton = () => { return ( } disablePadding sx={(theme) => ({ position: 'relative', flexDirection: 'row', alignItems: 'center', padding: 0, [`& .${listItemSecondaryActionClasses.root}`]: { right: theme.spacing(1.5), }, })} > } secondary={} /> ) } const TokenAmountSkeleton: React.FC = () => { return ( ) }