import { Add, Check, CopyAll, ExpandCircleDownOutlined, } from '@mui/icons-material' import clsx from 'clsx' import { useEffect, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { ButtonPopupSection, TokenCardProps, TokenType } from '@dao-dao/types' import { getFallbackImage, isNativeIbcUsdc, secondsToWdhms, shortenTokenSymbol, toAccessibleImageUrl, } from '@dao-dao/utils' import { useDaoIfAvailable } from '../../contexts' import { useAddToken } from '../../hooks' import { Button } from '../buttons/Button' import { CopyToClipboard } from '../CopyToClipboard' import { CrownIcon } from '../icons/CrownIcon' import { ButtonPopup } from '../popup' import { TooltipInfoIcon } from '../tooltip' import { Tooltip } from '../tooltip/Tooltip' import { TokenAmountDisplay } from './TokenAmountDisplay' import { UnstakingModal } from './UnstakingModal' export const TokenCard = ({ token, color, isGovernanceToken, subtitle, unstakedBalance, hasStakingInfo: _hasStakingInfo, lazyInfo, refreshUnstakingTasks, onClaim, ButtonLink, actions, EntityDisplay, }: TokenCardProps) => { const { t } = useTranslation() // If in a DAO context, don't show the DAOs governed section if the only DAO // this token governs is the current DAO. See the comment where this is used // for more details. const { coreAddress } = useDaoIfAvailable() ?? {} const lazyStakes = lazyInfo.loading || !lazyInfo.data.stakingInfo ? [] : lazyInfo.data.stakingInfo.stakes const totalStaked = lazyInfo.loading || !lazyInfo.data.stakingInfo ? HugeDecimal.zero : lazyInfo.data.stakingInfo.totalStaked const totalPendingRewards = lazyInfo.loading || !lazyInfo.data.stakingInfo ? HugeDecimal.zero : lazyInfo.data.stakingInfo.totalPendingRewards const totalUnstaking = lazyInfo.loading || !lazyInfo.data.stakingInfo ? HugeDecimal.zero : lazyInfo.data.stakingInfo.totalUnstaking const [showUnstakingTokens, setShowUnstakingTokens] = useState(false) const [copied, setCopied] = useState(false) // Debounce clearing copied. useEffect(() => { const timeout = setTimeout(() => setCopied(false), 2000) return () => clearTimeout(timeout) }, [copied]) const { isShortened, tokenSymbol, originalTokenSymbol } = shortenTokenSymbol( token.symbol ) const addToken = useAddToken() const addCw20Token = addToken && token.type === TokenType.Cw20 ? () => addToken(token.denomOrAddress) : undefined // Setup actions for popup. Prefill with cw20 related actions. const buttonPopupSections: ButtonPopupSection[] = [ { label: t('title.token'), buttons: [ { Icon: copied ? Check : CopyAll, label: token.type === TokenType.Cw20 ? t('button.copyAddressToClipboard') : t('button.copyIdToClipboard'), closeOnClick: false, onClick: () => { navigator.clipboard.writeText(token.denomOrAddress) toast.success(t('info.copiedToClipboard')) setCopied(true) }, }, ...(addCw20Token ? [ { Icon: Add, label: t('button.addToKeplr'), closeOnClick: false, onClick: addCw20Token, }, ] : []), ...(actions?.token ?? []), ], }, ...(actions?.extraSections ?? []), ] // This has staking info if we have already determined it has staking info, or // if there are any stakes or unstaking tasks once the data is loaded. For // efficiency, we don't load unstaking tasks right away because it depends on // several queries, but we can quickly check if there is anything staked and // preset `hasStakingInfo` if so. This makes sure that unstaking tasks show // even when there is nothing staked. const hasStakingInfo = _hasStakingInfo || (!lazyInfo.loading && (!!lazyInfo.data.stakingInfo?.stakes.length || !!lazyInfo.data.stakingInfo?.unstakingTasks.length)) const waitingForStakingInfo = hasStakingInfo && lazyInfo.loading return ( <>
${tokenSymbol}
)} {color && ( )}{subtitle}
}{t('info.totalHoldings')}
{/* leading-5 to match link-text's line-height. */}{t('info.availableBalance')}
{t('info.stakes')}
{t('title.staked')}
{t('title.stakedTo')}
{lazyInfo.loading
? '...'
: lazyStakes.length > 0 && (
<>
{lazyStakes[0].validator.moniker}
{lazyStakes.length > 1 && (
<>
,{' '}
{validator.moniker}
{t('title.unstaking')}
{t('info.pendingRewards')}
{t('title.daosGoverned')}